xref: /openbmc/linux/drivers/nvdimm/dimm_devs.c (revision 0e796e3e)
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 
214d88a97aSDan Williams /*
224d88a97aSDan Williams  * Retrieve bus and dimm handle and return if this bus supports
234d88a97aSDan Williams  * get_config_data commands
244d88a97aSDan Williams  */
nvdimm_check_config_data(struct device * dev)25aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev)
264d88a97aSDan Williams {
27aee65987SToshi Kani 	struct nvdimm *nvdimm = to_nvdimm(dev);
284d88a97aSDan Williams 
29aee65987SToshi Kani 	if (!nvdimm->cmd_mask ||
30aee65987SToshi Kani 	    !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
31a0e37452SDan Williams 		if (test_bit(NDD_LABELING, &nvdimm->flags))
324d88a97aSDan Williams 			return -ENXIO;
33aee65987SToshi Kani 		else
34aee65987SToshi Kani 			return -ENOTTY;
35aee65987SToshi Kani 	}
364d88a97aSDan Williams 
374d88a97aSDan Williams 	return 0;
384d88a97aSDan Williams }
394d88a97aSDan Williams 
validate_dimm(struct nvdimm_drvdata * ndd)404d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd)
414d88a97aSDan Williams {
42aee65987SToshi Kani 	int rc;
434d88a97aSDan Williams 
44aee65987SToshi Kani 	if (!ndd)
45aee65987SToshi Kani 		return -EINVAL;
46aee65987SToshi Kani 
47aee65987SToshi Kani 	rc = nvdimm_check_config_data(ndd->dev);
48aee65987SToshi Kani 	if (rc)
49d75f773cSSakari Ailus 		dev_dbg(ndd->dev, "%ps: %s error: %d\n",
504d88a97aSDan Williams 				__builtin_return_address(0), __func__, rc);
514d88a97aSDan Williams 	return rc;
524d88a97aSDan Williams }
534d88a97aSDan Williams 
544d88a97aSDan Williams /**
554d88a97aSDan Williams  * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
564d88a97aSDan Williams  * @nvdimm: dimm to initialize
574d88a97aSDan Williams  */
nvdimm_init_nsarea(struct nvdimm_drvdata * ndd)584d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
594d88a97aSDan Williams {
604d88a97aSDan Williams 	struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
614d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
624d88a97aSDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
634d88a97aSDan Williams 	int rc = validate_dimm(ndd);
649d62ed96SDan Williams 	int cmd_rc = 0;
654d88a97aSDan Williams 
664d88a97aSDan Williams 	if (rc)
674d88a97aSDan Williams 		return rc;
684d88a97aSDan Williams 
694d88a97aSDan Williams 	if (cmd->config_size)
704d88a97aSDan Williams 		return 0; /* already valid */
714d88a97aSDan Williams 
724d88a97aSDan Williams 	memset(cmd, 0, sizeof(*cmd));
734d88a97aSDan Williams 	nd_desc = nvdimm_bus->nd_desc;
749d62ed96SDan Williams 	rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
759d62ed96SDan Williams 			ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc);
769d62ed96SDan Williams 	if (rc < 0)
779d62ed96SDan Williams 		return rc;
789d62ed96SDan Williams 	return cmd_rc;
794d88a97aSDan Williams }
804d88a97aSDan Williams 
nvdimm_get_config_data(struct nvdimm_drvdata * ndd,void * buf,size_t offset,size_t len)812d657d17SAlexander Duyck int nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf,
822d657d17SAlexander Duyck 			   size_t offset, size_t len)
834d88a97aSDan Williams {
844d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
852d657d17SAlexander Duyck 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
86e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
874d88a97aSDan Williams 	struct nd_cmd_get_config_data_hdr *cmd;
882d657d17SAlexander Duyck 	size_t max_cmd_size, buf_offset;
894d88a97aSDan Williams 
904d88a97aSDan Williams 	if (rc)
914d88a97aSDan Williams 		return rc;
924d88a97aSDan Williams 
932d657d17SAlexander Duyck 	if (offset + len > ndd->nsarea.config_size)
944d88a97aSDan Williams 		return -ENXIO;
954d88a97aSDan Williams 
962d657d17SAlexander Duyck 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
97d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
984d88a97aSDan Williams 	if (!cmd)
994d88a97aSDan Williams 		return -ENOMEM;
1004d88a97aSDan Williams 
1012d657d17SAlexander Duyck 	for (buf_offset = 0; len;
1022d657d17SAlexander Duyck 	     len -= cmd->in_length, buf_offset += cmd->in_length) {
1032d657d17SAlexander Duyck 		size_t cmd_size;
1042d657d17SAlexander Duyck 
1052d657d17SAlexander Duyck 		cmd->in_offset = offset + buf_offset;
1062d657d17SAlexander Duyck 		cmd->in_length = min(max_cmd_size, len);
1072d657d17SAlexander Duyck 
1082d657d17SAlexander Duyck 		cmd_size = sizeof(*cmd) + cmd->in_length;
1092d657d17SAlexander Duyck 
1104d88a97aSDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
1112d657d17SAlexander Duyck 				ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
112e7c5a571SDan Williams 		if (rc < 0)
113e7c5a571SDan Williams 			break;
114e7c5a571SDan Williams 		if (cmd_rc < 0) {
115e7c5a571SDan Williams 			rc = cmd_rc;
1164d88a97aSDan Williams 			break;
1174d88a97aSDan Williams 		}
1182d657d17SAlexander Duyck 
1192d657d17SAlexander Duyck 		/* out_buf should be valid, copy it into our output buffer */
1202d657d17SAlexander Duyck 		memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length);
1214d88a97aSDan Williams 	}
122d11cf4a7SDan Williams 	kvfree(cmd);
1234d88a97aSDan Williams 
1244d88a97aSDan Williams 	return rc;
1254d88a97aSDan Williams }
1264d88a97aSDan Williams 
nvdimm_set_config_data(struct nvdimm_drvdata * ndd,size_t offset,void * buf,size_t len)127f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
128f524bf27SDan Williams 		void *buf, size_t len)
129f524bf27SDan Williams {
130f524bf27SDan Williams 	size_t max_cmd_size, buf_offset;
131f524bf27SDan Williams 	struct nd_cmd_set_config_hdr *cmd;
132e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
133f524bf27SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
134f524bf27SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
135f524bf27SDan Williams 
136f524bf27SDan Williams 	if (rc)
137f524bf27SDan Williams 		return rc;
138f524bf27SDan Williams 
139f524bf27SDan Williams 	if (offset + len > ndd->nsarea.config_size)
140f524bf27SDan Williams 		return -ENXIO;
141f524bf27SDan Williams 
142d11cf4a7SDan Williams 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
143d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
144f524bf27SDan Williams 	if (!cmd)
145f524bf27SDan Williams 		return -ENOMEM;
146f524bf27SDan Williams 
147f524bf27SDan Williams 	for (buf_offset = 0; len; len -= cmd->in_length,
148f524bf27SDan Williams 			buf_offset += cmd->in_length) {
149f524bf27SDan Williams 		size_t cmd_size;
150f524bf27SDan Williams 
151f524bf27SDan Williams 		cmd->in_offset = offset + buf_offset;
152f524bf27SDan Williams 		cmd->in_length = min(max_cmd_size, len);
153f524bf27SDan Williams 		memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
154f524bf27SDan Williams 
155f524bf27SDan Williams 		/* status is output in the last 4-bytes of the command buffer */
156f524bf27SDan Williams 		cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
157f524bf27SDan Williams 
158f524bf27SDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
159e7c5a571SDan Williams 				ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
160e7c5a571SDan Williams 		if (rc < 0)
161e7c5a571SDan Williams 			break;
162e7c5a571SDan Williams 		if (cmd_rc < 0) {
163e7c5a571SDan Williams 			rc = cmd_rc;
164f524bf27SDan Williams 			break;
165f524bf27SDan Williams 		}
166f524bf27SDan Williams 	}
167d11cf4a7SDan Williams 	kvfree(cmd);
168f524bf27SDan Williams 
169f524bf27SDan Williams 	return rc;
170f524bf27SDan Williams }
171f524bf27SDan Williams 
nvdimm_set_labeling(struct device * dev)172a0e37452SDan Williams void nvdimm_set_labeling(struct device *dev)
17342237e39SDan Williams {
17442237e39SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
17542237e39SDan Williams 
176a0e37452SDan Williams 	set_bit(NDD_LABELING, &nvdimm->flags);
1778f078b38SDan Williams }
1788f078b38SDan Williams 
nvdimm_set_locked(struct device * dev)1798f078b38SDan Williams void nvdimm_set_locked(struct device *dev)
1808f078b38SDan Williams {
1818f078b38SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1828f078b38SDan Williams 
1838f078b38SDan Williams 	set_bit(NDD_LOCKED, &nvdimm->flags);
18442237e39SDan Williams }
18542237e39SDan Williams 
nvdimm_clear_locked(struct device * dev)186d34cb808SDan Williams void nvdimm_clear_locked(struct device *dev)
187d34cb808SDan Williams {
188d34cb808SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
189d34cb808SDan Williams 
190d34cb808SDan Williams 	clear_bit(NDD_LOCKED, &nvdimm->flags);
191d34cb808SDan Williams }
192d34cb808SDan Williams 
nvdimm_release(struct device * dev)193e6dfb2deSDan Williams static void nvdimm_release(struct device *dev)
194e6dfb2deSDan Williams {
195e6dfb2deSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
196e6dfb2deSDan Williams 
197e6dfb2deSDan Williams 	ida_simple_remove(&dimm_ida, nvdimm->id);
198e6dfb2deSDan Williams 	kfree(nvdimm);
199e6dfb2deSDan Williams }
200e6dfb2deSDan Williams 
to_nvdimm(struct device * dev)201e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev)
202e6dfb2deSDan Williams {
203e6dfb2deSDan Williams 	struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
204e6dfb2deSDan Williams 
205e6dfb2deSDan Williams 	WARN_ON(!is_nvdimm(dev));
206e6dfb2deSDan Williams 	return nvdimm;
207e6dfb2deSDan Williams }
208e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm);
209e6dfb2deSDan Williams 
to_ndd(struct nd_mapping * nd_mapping)210bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
211bf9bccc1SDan Williams {
212bf9bccc1SDan Williams 	struct nvdimm *nvdimm = nd_mapping->nvdimm;
213bf9bccc1SDan Williams 
214bf9bccc1SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
215bf9bccc1SDan Williams 
216bf9bccc1SDan Williams 	return dev_get_drvdata(&nvdimm->dev);
217bf9bccc1SDan Williams }
218bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd);
219bf9bccc1SDan Williams 
nvdimm_drvdata_release(struct kref * kref)220bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref)
221bf9bccc1SDan Williams {
222bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
223bf9bccc1SDan Williams 	struct device *dev = ndd->dev;
224bf9bccc1SDan Williams 	struct resource *res, *_r;
225bf9bccc1SDan Williams 
226426824d6SDan Williams 	dev_dbg(dev, "trace\n");
227bf9bccc1SDan Williams 	nvdimm_bus_lock(dev);
228bf9bccc1SDan Williams 	for_each_dpa_resource_safe(ndd, res, _r)
229bf9bccc1SDan Williams 		nvdimm_free_dpa(ndd, res);
230bf9bccc1SDan Williams 	nvdimm_bus_unlock(dev);
231bf9bccc1SDan Williams 
232a06a7576Syalin wang 	kvfree(ndd->data);
233bf9bccc1SDan Williams 	kfree(ndd);
234bf9bccc1SDan Williams 	put_device(dev);
235bf9bccc1SDan Williams }
236bf9bccc1SDan Williams 
get_ndd(struct nvdimm_drvdata * ndd)237bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd)
238bf9bccc1SDan Williams {
239bf9bccc1SDan Williams 	kref_get(&ndd->kref);
240bf9bccc1SDan Williams }
241bf9bccc1SDan Williams 
put_ndd(struct nvdimm_drvdata * ndd)242bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd)
243bf9bccc1SDan Williams {
244bf9bccc1SDan Williams 	if (ndd)
245bf9bccc1SDan Williams 		kref_put(&ndd->kref, nvdimm_drvdata_release);
246bf9bccc1SDan Williams }
247bf9bccc1SDan Williams 
nvdimm_name(struct nvdimm * nvdimm)248e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm)
249e6dfb2deSDan Williams {
250e6dfb2deSDan Williams 	return dev_name(&nvdimm->dev);
251e6dfb2deSDan Williams }
252e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name);
253e6dfb2deSDan Williams 
nvdimm_kobj(struct nvdimm * nvdimm)254ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
255ba9c8dd3SDan Williams {
256ba9c8dd3SDan Williams 	return &nvdimm->dev.kobj;
257ba9c8dd3SDan Williams }
258ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj);
259ba9c8dd3SDan Williams 
nvdimm_cmd_mask(struct nvdimm * nvdimm)260e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
261e3654ecaSDan Williams {
262e3654ecaSDan Williams 	return nvdimm->cmd_mask;
263e3654ecaSDan Williams }
264e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
265e3654ecaSDan Williams 
nvdimm_provider_data(struct nvdimm * nvdimm)266e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm)
267e6dfb2deSDan Williams {
26862232e45SDan Williams 	if (nvdimm)
269e6dfb2deSDan Williams 		return nvdimm->provider_data;
27062232e45SDan Williams 	return NULL;
271e6dfb2deSDan Williams }
272e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data);
273e6dfb2deSDan Williams 
commands_show(struct device * dev,struct device_attribute * attr,char * buf)27462232e45SDan Williams static ssize_t commands_show(struct device *dev,
27562232e45SDan Williams 		struct device_attribute *attr, char *buf)
27662232e45SDan Williams {
27762232e45SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
27862232e45SDan Williams 	int cmd, len = 0;
27962232e45SDan Williams 
280e3654ecaSDan Williams 	if (!nvdimm->cmd_mask)
28162232e45SDan Williams 		return sprintf(buf, "\n");
28262232e45SDan Williams 
283e3654ecaSDan Williams 	for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
28462232e45SDan Williams 		len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
28562232e45SDan Williams 	len += sprintf(buf + len, "\n");
28662232e45SDan Williams 	return len;
28762232e45SDan Williams }
28862232e45SDan Williams static DEVICE_ATTR_RO(commands);
28962232e45SDan Williams 
flags_show(struct device * dev,struct device_attribute * attr,char * buf)290efbf6f50SDan Williams static ssize_t flags_show(struct device *dev,
291efbf6f50SDan Williams 		struct device_attribute *attr, char *buf)
292efbf6f50SDan Williams {
293efbf6f50SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
294efbf6f50SDan Williams 
2953b6c6c03SDan Williams 	return sprintf(buf, "%s%s\n",
296a0e37452SDan Williams 			test_bit(NDD_LABELING, &nvdimm->flags) ? "label " : "",
297efbf6f50SDan Williams 			test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : "");
298efbf6f50SDan Williams }
299efbf6f50SDan Williams static DEVICE_ATTR_RO(flags);
300efbf6f50SDan Williams 
state_show(struct device * dev,struct device_attribute * attr,char * buf)301eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr,
302eaf96153SDan Williams 		char *buf)
303eaf96153SDan Williams {
304eaf96153SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
305eaf96153SDan Williams 
306eaf96153SDan Williams 	/*
307eaf96153SDan Williams 	 * The state may be in the process of changing, userspace should
308eaf96153SDan Williams 	 * quiesce probing if it wants a static answer
309eaf96153SDan Williams 	 */
310eaf96153SDan Williams 	nvdimm_bus_lock(dev);
311eaf96153SDan Williams 	nvdimm_bus_unlock(dev);
312eaf96153SDan Williams 	return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy)
313eaf96153SDan Williams 			? "active" : "idle");
314eaf96153SDan Williams }
315eaf96153SDan Williams static DEVICE_ATTR_RO(state);
316eaf96153SDan Williams 
__available_slots_show(struct nvdimm_drvdata * ndd,char * buf)3177018c897SDan Williams static ssize_t __available_slots_show(struct nvdimm_drvdata *ndd, char *buf)
3180ba1c634SDan Williams {
3197018c897SDan Williams 	struct device *dev;
3200ba1c634SDan Williams 	ssize_t rc;
3210ba1c634SDan Williams 	u32 nfree;
3220ba1c634SDan Williams 
3230ba1c634SDan Williams 	if (!ndd)
3240ba1c634SDan Williams 		return -ENXIO;
3250ba1c634SDan Williams 
3267018c897SDan Williams 	dev = ndd->dev;
3270ba1c634SDan Williams 	nvdimm_bus_lock(dev);
3280ba1c634SDan Williams 	nfree = nd_label_nfree(ndd);
3290ba1c634SDan Williams 	if (nfree - 1 > nfree) {
3300ba1c634SDan Williams 		dev_WARN_ONCE(dev, 1, "we ate our last label?\n");
3310ba1c634SDan Williams 		nfree = 0;
3320ba1c634SDan Williams 	} else
3330ba1c634SDan Williams 		nfree--;
3340ba1c634SDan Williams 	rc = sprintf(buf, "%d\n", nfree);
3350ba1c634SDan Williams 	nvdimm_bus_unlock(dev);
3360ba1c634SDan Williams 	return rc;
3370ba1c634SDan Williams }
3387018c897SDan Williams 
available_slots_show(struct device * dev,struct device_attribute * attr,char * buf)3397018c897SDan Williams static ssize_t available_slots_show(struct device *dev,
3407018c897SDan Williams 				    struct device_attribute *attr, char *buf)
3417018c897SDan Williams {
3427018c897SDan Williams 	ssize_t rc;
3437018c897SDan Williams 
34481beea55SDan Williams 	device_lock(dev);
3457018c897SDan Williams 	rc = __available_slots_show(dev_get_drvdata(dev), buf);
34681beea55SDan Williams 	device_unlock(dev);
3477018c897SDan Williams 
3487018c897SDan Williams 	return rc;
3497018c897SDan Williams }
3500ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots);
3510ba1c634SDan Williams 
security_show(struct device * dev,struct device_attribute * attr,char * buf)352*7f80ab36SArnd Bergmann static ssize_t security_show(struct device *dev,
353f2989396SDave Jiang 			     struct device_attribute *attr, char *buf)
354f2989396SDave Jiang {
355f2989396SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
356f2989396SDave Jiang 
35715a83487SDave Jiang 	/*
35815a83487SDave Jiang 	 * For the test version we need to poll the "hardware" in order
35915a83487SDave Jiang 	 * to get the updated status for unlock testing.
36015a83487SDave Jiang 	 */
36115a83487SDave Jiang 	if (IS_ENABLED(CONFIG_NVDIMM_SECURITY_TEST))
36215a83487SDave Jiang 		nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
36315a83487SDave Jiang 
3647c02d53dSJane Chu 	if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
3657c02d53dSJane Chu 		return sprintf(buf, "overwrite\n");
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");
37289fa9d8eSDave Jiang 	return -ENOTTY;
373f2989396SDave Jiang }
374f2989396SDave Jiang 
frozen_show(struct device * dev,struct device_attribute * attr,char * buf)375d78c620aSDan Williams static ssize_t frozen_show(struct device *dev,
376d78c620aSDan Williams 		struct device_attribute *attr, char *buf)
377d78c620aSDan Williams {
378d78c620aSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
379d78c620aSDan Williams 
380d78c620aSDan Williams 	return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
381d78c620aSDan Williams 				&nvdimm->sec.flags));
382f2989396SDave Jiang }
383d78c620aSDan Williams static DEVICE_ATTR_RO(frozen);
38437833fb7SDave Jiang 
security_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)38537833fb7SDave Jiang static ssize_t security_store(struct device *dev,
38637833fb7SDave Jiang 		struct device_attribute *attr, const char *buf, size_t len)
38737833fb7SDave Jiang 
38837833fb7SDave Jiang {
38937833fb7SDave Jiang 	ssize_t rc;
39037833fb7SDave Jiang 
39137833fb7SDave Jiang 	/*
39237833fb7SDave Jiang 	 * Require all userspace triggered security management to be
39337833fb7SDave Jiang 	 * done while probing is idle and the DIMM is not in active use
39437833fb7SDave Jiang 	 * in any region.
39537833fb7SDave Jiang 	 */
39681beea55SDan Williams 	device_lock(dev);
39737833fb7SDave Jiang 	nvdimm_bus_lock(dev);
39837833fb7SDave Jiang 	wait_nvdimm_bus_probe_idle(dev);
3997b60422cSDan Williams 	rc = nvdimm_security_store(dev, buf, len);
40037833fb7SDave Jiang 	nvdimm_bus_unlock(dev);
40181beea55SDan Williams 	device_unlock(dev);
40237833fb7SDave Jiang 
40337833fb7SDave Jiang 	return rc;
40437833fb7SDave Jiang }
40537833fb7SDave Jiang static DEVICE_ATTR_RW(security);
406f2989396SDave Jiang 
40762232e45SDan Williams static struct attribute *nvdimm_attributes[] = {
408eaf96153SDan Williams 	&dev_attr_state.attr,
409efbf6f50SDan Williams 	&dev_attr_flags.attr,
41062232e45SDan Williams 	&dev_attr_commands.attr,
4110ba1c634SDan Williams 	&dev_attr_available_slots.attr,
412f2989396SDave Jiang 	&dev_attr_security.attr,
413d78c620aSDan Williams 	&dev_attr_frozen.attr,
41462232e45SDan Williams 	NULL,
41562232e45SDan Williams };
41662232e45SDan Williams 
nvdimm_visible(struct kobject * kobj,struct attribute * a,int n)417f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
418f2989396SDave Jiang {
419f2989396SDave Jiang 	struct device *dev = container_of(kobj, typeof(*dev), kobj);
420f2989396SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
421f2989396SDave Jiang 
422d78c620aSDan Williams 	if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
423f2989396SDave Jiang 		return a->mode;
424d78c620aSDan Williams 	if (!nvdimm->sec.flags)
425f2989396SDave Jiang 		return 0;
426d78c620aSDan Williams 
427d78c620aSDan Williams 	if (a == &dev_attr_security.attr) {
428d78c620aSDan Williams 		/* Are there any state mutation ops (make writable)? */
429d2a4ac73SDave Jiang 		if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
43064e77c8cSDave Jiang 				|| nvdimm->sec.ops->change_key
4317d988097SDave Jiang 				|| nvdimm->sec.ops->erase
4327d988097SDave Jiang 				|| nvdimm->sec.ops->overwrite)
433f2989396SDave Jiang 			return a->mode;
43437833fb7SDave Jiang 		return 0444;
435f2989396SDave Jiang 	}
436f2989396SDave Jiang 
437d78c620aSDan Williams 	if (nvdimm->sec.ops->freeze)
438d78c620aSDan Williams 		return a->mode;
439d78c620aSDan Williams 	return 0;
440d78c620aSDan Williams }
441d78c620aSDan Williams 
442360eba7eSDan Williams static const struct attribute_group nvdimm_attribute_group = {
44362232e45SDan Williams 	.attrs = nvdimm_attributes,
444f2989396SDave Jiang 	.is_visible = nvdimm_visible,
44562232e45SDan Williams };
446360eba7eSDan Williams 
result_show(struct device * dev,struct device_attribute * attr,char * buf)44748001ea5SDan Williams static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf)
44848001ea5SDan Williams {
44948001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
45048001ea5SDan Williams 	enum nvdimm_fwa_result result;
45148001ea5SDan Williams 
45248001ea5SDan Williams 	if (!nvdimm->fw_ops)
45348001ea5SDan Williams 		return -EOPNOTSUPP;
45448001ea5SDan Williams 
45548001ea5SDan Williams 	nvdimm_bus_lock(dev);
45648001ea5SDan Williams 	result = nvdimm->fw_ops->activate_result(nvdimm);
45748001ea5SDan Williams 	nvdimm_bus_unlock(dev);
45848001ea5SDan Williams 
45948001ea5SDan Williams 	switch (result) {
46048001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NONE:
46148001ea5SDan Williams 		return sprintf(buf, "none\n");
46248001ea5SDan Williams 	case NVDIMM_FWA_RESULT_SUCCESS:
46348001ea5SDan Williams 		return sprintf(buf, "success\n");
46448001ea5SDan Williams 	case NVDIMM_FWA_RESULT_FAIL:
46548001ea5SDan Williams 		return sprintf(buf, "fail\n");
46648001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NOTSTAGED:
46748001ea5SDan Williams 		return sprintf(buf, "not_staged\n");
46848001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NEEDRESET:
46948001ea5SDan Williams 		return sprintf(buf, "need_reset\n");
47048001ea5SDan Williams 	default:
47148001ea5SDan Williams 		return -ENXIO;
47248001ea5SDan Williams 	}
47348001ea5SDan Williams }
47448001ea5SDan Williams static DEVICE_ATTR_ADMIN_RO(result);
47548001ea5SDan Williams 
activate_show(struct device * dev,struct device_attribute * attr,char * buf)47648001ea5SDan Williams static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf)
47748001ea5SDan Williams {
47848001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
47948001ea5SDan Williams 	enum nvdimm_fwa_state state;
48048001ea5SDan Williams 
48148001ea5SDan Williams 	if (!nvdimm->fw_ops)
48248001ea5SDan Williams 		return -EOPNOTSUPP;
48348001ea5SDan Williams 
48448001ea5SDan Williams 	nvdimm_bus_lock(dev);
48548001ea5SDan Williams 	state = nvdimm->fw_ops->activate_state(nvdimm);
48648001ea5SDan Williams 	nvdimm_bus_unlock(dev);
48748001ea5SDan Williams 
48848001ea5SDan Williams 	switch (state) {
48948001ea5SDan Williams 	case NVDIMM_FWA_IDLE:
49048001ea5SDan Williams 		return sprintf(buf, "idle\n");
49148001ea5SDan Williams 	case NVDIMM_FWA_BUSY:
49248001ea5SDan Williams 		return sprintf(buf, "busy\n");
49348001ea5SDan Williams 	case NVDIMM_FWA_ARMED:
49448001ea5SDan Williams 		return sprintf(buf, "armed\n");
49548001ea5SDan Williams 	default:
49648001ea5SDan Williams 		return -ENXIO;
49748001ea5SDan Williams 	}
49848001ea5SDan Williams }
49948001ea5SDan Williams 
activate_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)50048001ea5SDan Williams static ssize_t activate_store(struct device *dev, struct device_attribute *attr,
50148001ea5SDan Williams 		const char *buf, size_t len)
50248001ea5SDan Williams {
50348001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
50448001ea5SDan Williams 	enum nvdimm_fwa_trigger arg;
50548001ea5SDan Williams 	int rc;
50648001ea5SDan Williams 
50748001ea5SDan Williams 	if (!nvdimm->fw_ops)
50848001ea5SDan Williams 		return -EOPNOTSUPP;
50948001ea5SDan Williams 
51048001ea5SDan Williams 	if (sysfs_streq(buf, "arm"))
51148001ea5SDan Williams 		arg = NVDIMM_FWA_ARM;
51248001ea5SDan Williams 	else if (sysfs_streq(buf, "disarm"))
51348001ea5SDan Williams 		arg = NVDIMM_FWA_DISARM;
51448001ea5SDan Williams 	else
51548001ea5SDan Williams 		return -EINVAL;
51648001ea5SDan Williams 
51748001ea5SDan Williams 	nvdimm_bus_lock(dev);
51848001ea5SDan Williams 	rc = nvdimm->fw_ops->arm(nvdimm, arg);
51948001ea5SDan Williams 	nvdimm_bus_unlock(dev);
52048001ea5SDan Williams 
52148001ea5SDan Williams 	if (rc < 0)
52248001ea5SDan Williams 		return rc;
52348001ea5SDan Williams 	return len;
52448001ea5SDan Williams }
52548001ea5SDan Williams static DEVICE_ATTR_ADMIN_RW(activate);
52648001ea5SDan Williams 
52748001ea5SDan Williams static struct attribute *nvdimm_firmware_attributes[] = {
52848001ea5SDan Williams 	&dev_attr_activate.attr,
52948001ea5SDan Williams 	&dev_attr_result.attr,
53062c78927SZqiang 	NULL,
53148001ea5SDan Williams };
53248001ea5SDan Williams 
nvdimm_firmware_visible(struct kobject * kobj,struct attribute * a,int n)53348001ea5SDan Williams static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
53448001ea5SDan Williams {
53548001ea5SDan Williams 	struct device *dev = container_of(kobj, typeof(*dev), kobj);
53648001ea5SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
53748001ea5SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
53848001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
53948001ea5SDan Williams 	enum nvdimm_fwa_capability cap;
54048001ea5SDan Williams 
54148001ea5SDan Williams 	if (!nd_desc->fw_ops)
54248001ea5SDan Williams 		return 0;
54348001ea5SDan Williams 	if (!nvdimm->fw_ops)
54448001ea5SDan Williams 		return 0;
54548001ea5SDan Williams 
54648001ea5SDan Williams 	nvdimm_bus_lock(dev);
54748001ea5SDan Williams 	cap = nd_desc->fw_ops->capability(nd_desc);
54848001ea5SDan Williams 	nvdimm_bus_unlock(dev);
54948001ea5SDan Williams 
55048001ea5SDan Williams 	if (cap < NVDIMM_FWA_CAP_QUIESCE)
55148001ea5SDan Williams 		return 0;
55248001ea5SDan Williams 
55348001ea5SDan Williams 	return a->mode;
55448001ea5SDan Williams }
55548001ea5SDan Williams 
55648001ea5SDan Williams static const struct attribute_group nvdimm_firmware_attribute_group = {
55748001ea5SDan Williams 	.name = "firmware",
55848001ea5SDan Williams 	.attrs = nvdimm_firmware_attributes,
55948001ea5SDan Williams 	.is_visible = nvdimm_firmware_visible,
56048001ea5SDan Williams };
56148001ea5SDan Williams 
562360eba7eSDan Williams static const struct attribute_group *nvdimm_attribute_groups[] = {
563360eba7eSDan Williams 	&nd_device_attribute_group,
564360eba7eSDan Williams 	&nvdimm_attribute_group,
56548001ea5SDan Williams 	&nvdimm_firmware_attribute_group,
566360eba7eSDan Williams 	NULL,
567360eba7eSDan Williams };
568360eba7eSDan Williams 
569360eba7eSDan Williams static const struct device_type nvdimm_device_type = {
570360eba7eSDan Williams 	.name = "nvdimm",
571360eba7eSDan Williams 	.release = nvdimm_release,
572360eba7eSDan Williams 	.groups = nvdimm_attribute_groups,
573360eba7eSDan Williams };
574360eba7eSDan Williams 
is_nvdimm(const struct device * dev)5752a81ada3SGreg Kroah-Hartman bool is_nvdimm(const struct device *dev)
576360eba7eSDan Williams {
577360eba7eSDan Williams 	return dev->type == &nvdimm_device_type;
578360eba7eSDan Williams }
57962232e45SDan Williams 
5804a0079bcSDan Williams static struct lock_class_key nvdimm_key;
5814a0079bcSDan Williams 
__nvdimm_create(struct nvdimm_bus * nvdimm_bus,void * provider_data,const struct attribute_group ** groups,unsigned long flags,unsigned long cmd_mask,int num_flush,struct resource * flush_wpq,const char * dimm_id,const struct nvdimm_security_ops * sec_ops,const struct nvdimm_fw_ops * fw_ops)582d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
583d6548ae4SDave Jiang 		void *provider_data, const struct attribute_group **groups,
584d6548ae4SDave Jiang 		unsigned long flags, unsigned long cmd_mask, int num_flush,
585f2989396SDave Jiang 		struct resource *flush_wpq, const char *dimm_id,
586a1facc1fSDan Williams 		const struct nvdimm_security_ops *sec_ops,
587a1facc1fSDan Williams 		const struct nvdimm_fw_ops *fw_ops)
588e6dfb2deSDan Williams {
589e6dfb2deSDan Williams 	struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
590e6dfb2deSDan Williams 	struct device *dev;
591e6dfb2deSDan Williams 
592e6dfb2deSDan Williams 	if (!nvdimm)
593e6dfb2deSDan Williams 		return NULL;
594e6dfb2deSDan Williams 
595e6dfb2deSDan Williams 	nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
596e6dfb2deSDan Williams 	if (nvdimm->id < 0) {
597e6dfb2deSDan Williams 		kfree(nvdimm);
598e6dfb2deSDan Williams 		return NULL;
599e6dfb2deSDan Williams 	}
600d6548ae4SDave Jiang 
601d6548ae4SDave Jiang 	nvdimm->dimm_id = dimm_id;
602e6dfb2deSDan Williams 	nvdimm->provider_data = provider_data;
603e6dfb2deSDan Williams 	nvdimm->flags = flags;
604e3654ecaSDan Williams 	nvdimm->cmd_mask = cmd_mask;
605e5ae3b25SDan Williams 	nvdimm->num_flush = num_flush;
606e5ae3b25SDan Williams 	nvdimm->flush_wpq = flush_wpq;
607eaf96153SDan Williams 	atomic_set(&nvdimm->busy, 0);
608e6dfb2deSDan Williams 	dev = &nvdimm->dev;
609e6dfb2deSDan Williams 	dev_set_name(dev, "nmem%d", nvdimm->id);
610e6dfb2deSDan Williams 	dev->parent = &nvdimm_bus->dev;
611e6dfb2deSDan Williams 	dev->type = &nvdimm_device_type;
61262232e45SDan Williams 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
613e6dfb2deSDan Williams 	dev->groups = groups;
614f2989396SDave Jiang 	nvdimm->sec.ops = sec_ops;
615a1facc1fSDan Williams 	nvdimm->fw_ops = fw_ops;
6167d988097SDave Jiang 	nvdimm->sec.overwrite_tmo = 0;
6177d988097SDave Jiang 	INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query);
618f2989396SDave Jiang 	/*
619f2989396SDave Jiang 	 * Security state must be initialized before device_add() for
620f2989396SDave Jiang 	 * attribute visibility.
621f2989396SDave Jiang 	 */
62289fa9d8eSDave Jiang 	/* get security state and extended (master) state */
623d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
624d78c620aSDan Williams 	nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
6254a0079bcSDan Williams 	device_initialize(dev);
6264a0079bcSDan Williams 	lockdep_set_class(&dev->mutex, &nvdimm_key);
627f57aec44SDan Williams 	if (test_bit(NDD_REGISTER_SYNC, &flags))
628f57aec44SDan Williams 		nd_device_register_sync(dev);
629f57aec44SDan Williams 	else
6304d88a97aSDan Williams 		nd_device_register(dev);
631e6dfb2deSDan Williams 
632e6dfb2deSDan Williams 	return nvdimm;
633e6dfb2deSDan Williams }
634d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create);
6354d88a97aSDan Williams 
nvdimm_delete(struct nvdimm * nvdimm)636fd14602dSDan Williams void nvdimm_delete(struct nvdimm *nvdimm)
637fd14602dSDan Williams {
638fd14602dSDan Williams 	struct device *dev = &nvdimm->dev;
639fd14602dSDan Williams 	bool dev_put = false;
640fd14602dSDan Williams 
641fd14602dSDan Williams 	/* We are shutting down. Make state frozen artificially. */
642fd14602dSDan Williams 	nvdimm_bus_lock(dev);
643fd14602dSDan Williams 	set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
644fd14602dSDan Williams 	if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
645fd14602dSDan Williams 		dev_put = true;
646fd14602dSDan Williams 	nvdimm_bus_unlock(dev);
647fd14602dSDan Williams 	cancel_delayed_work_sync(&nvdimm->dwork);
648fd14602dSDan Williams 	if (dev_put)
649fd14602dSDan Williams 		put_device(dev);
650fd14602dSDan Williams 	nd_device_unregister(dev, ND_SYNC);
651fd14602dSDan Williams }
652fd14602dSDan Williams EXPORT_SYMBOL_GPL(nvdimm_delete);
653fd14602dSDan Williams 
shutdown_security_notify(void * data)6541cd73865SDan Williams static void shutdown_security_notify(void *data)
6557d988097SDave Jiang {
6561cd73865SDan Williams 	struct nvdimm *nvdimm = data;
6571cd73865SDan Williams 
6581cd73865SDan Williams 	sysfs_put(nvdimm->sec.overwrite_state);
6591cd73865SDan Williams }
6601cd73865SDan Williams 
nvdimm_security_setup_events(struct device * dev)6611cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev)
6621cd73865SDan Williams {
6631cd73865SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
6641cd73865SDan Williams 
665d78c620aSDan Williams 	if (!nvdimm->sec.flags || !nvdimm->sec.ops
6661cd73865SDan Williams 			|| !nvdimm->sec.ops->overwrite)
6677d988097SDave Jiang 		return 0;
6681cd73865SDan Williams 	nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
6691cd73865SDan Williams 	if (!nvdimm->sec.overwrite_state)
6701cd73865SDan Williams 		return -ENOMEM;
6711cd73865SDan Williams 
6721cd73865SDan Williams 	return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm);
6737d988097SDave Jiang }
6747d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events);
6757d988097SDave Jiang 
nvdimm_in_overwrite(struct nvdimm * nvdimm)6767d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm)
6777d988097SDave Jiang {
6787d988097SDave Jiang 	return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
6797d988097SDave Jiang }
6807d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite);
6817d988097SDave Jiang 
nvdimm_security_freeze(struct nvdimm * nvdimm)68237833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm)
68337833fb7SDave Jiang {
68437833fb7SDave Jiang 	int rc;
68537833fb7SDave Jiang 
68637833fb7SDave Jiang 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
68737833fb7SDave Jiang 
68837833fb7SDave Jiang 	if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
68937833fb7SDave Jiang 		return -EOPNOTSUPP;
69037833fb7SDave Jiang 
691d78c620aSDan Williams 	if (!nvdimm->sec.flags)
69237833fb7SDave Jiang 		return -EIO;
69337833fb7SDave Jiang 
6947d988097SDave Jiang 	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
6957d988097SDave Jiang 		dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n");
6967d988097SDave Jiang 		return -EBUSY;
6977d988097SDave Jiang 	}
6987d988097SDave Jiang 
69937833fb7SDave Jiang 	rc = nvdimm->sec.ops->freeze(nvdimm);
700d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
70137833fb7SDave Jiang 
70237833fb7SDave Jiang 	return rc;
70337833fb7SDave Jiang }
70437833fb7SDave Jiang 
dpa_align(struct nd_region * nd_region)7052522afb8SDan Williams static unsigned long dpa_align(struct nd_region *nd_region)
7062522afb8SDan Williams {
7072522afb8SDan Williams 	struct device *dev = &nd_region->dev;
7082522afb8SDan Williams 
7092522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev),
7102522afb8SDan Williams 				"bus lock required for capacity provision\n"))
7112522afb8SDan Williams 		return 0;
7122522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align
7132522afb8SDan Williams 				% nd_region->ndr_mappings,
7142522afb8SDan Williams 				"invalid region align %#lx mappings: %d\n",
7152522afb8SDan Williams 				nd_region->align, nd_region->ndr_mappings))
7162522afb8SDan Williams 		return 0;
7172522afb8SDan Williams 	return nd_region->align / nd_region->ndr_mappings;
7182522afb8SDan Williams }
7192522afb8SDan Williams 
7201b40e09aSDan Williams /**
72112e3129eSKeith Busch  * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max
72212e3129eSKeith Busch  *			   contiguous unallocated dpa range.
72312e3129eSKeith Busch  * @nd_region: constrain available space check to this reference region
72412e3129eSKeith Busch  * @nd_mapping: container of dpa-resource-root + labels
72512e3129eSKeith Busch  */
nd_pmem_max_contiguous_dpa(struct nd_region * nd_region,struct nd_mapping * nd_mapping)72612e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
72712e3129eSKeith Busch 					   struct nd_mapping *nd_mapping)
72812e3129eSKeith Busch {
72912e3129eSKeith Busch 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
73012e3129eSKeith Busch 	struct nvdimm_bus *nvdimm_bus;
73112e3129eSKeith Busch 	resource_size_t max = 0;
73212e3129eSKeith Busch 	struct resource *res;
7332522afb8SDan Williams 	unsigned long align;
73412e3129eSKeith Busch 
73512e3129eSKeith Busch 	/* if a dimm is disabled the available capacity is zero */
73612e3129eSKeith Busch 	if (!ndd)
73712e3129eSKeith Busch 		return 0;
73812e3129eSKeith Busch 
7392522afb8SDan Williams 	align = dpa_align(nd_region);
7402522afb8SDan Williams 	if (!align)
7412522afb8SDan Williams 		return 0;
7422522afb8SDan Williams 
74312e3129eSKeith Busch 	nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
74412e3129eSKeith Busch 	if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm))
74512e3129eSKeith Busch 		return 0;
74612e3129eSKeith Busch 	for_each_dpa_resource(ndd, res) {
7472522afb8SDan Williams 		resource_size_t start, end;
7482522afb8SDan Williams 
74912e3129eSKeith Busch 		if (strcmp(res->name, "pmem-reserve") != 0)
75012e3129eSKeith Busch 			continue;
7512522afb8SDan Williams 		/* trim free space relative to current alignment setting */
7522522afb8SDan Williams 		start = ALIGN(res->start, align);
7532522afb8SDan Williams 		end = ALIGN_DOWN(res->end + 1, align) - 1;
7542522afb8SDan Williams 		if (end < start)
7552522afb8SDan Williams 			continue;
7562522afb8SDan Williams 		if (end - start + 1 > max)
7572522afb8SDan Williams 			max = end - start + 1;
75812e3129eSKeith Busch 	}
75912e3129eSKeith Busch 	release_free_pmem(nvdimm_bus, nd_mapping);
76012e3129eSKeith Busch 	return max;
76112e3129eSKeith Busch }
76212e3129eSKeith Busch 
76312e3129eSKeith Busch /**
764bf9bccc1SDan Williams  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
765bf9bccc1SDan Williams  * @nd_mapping: container of dpa-resource-root + labels
766bf9bccc1SDan Williams  * @nd_region: constrain available space check to this reference region
767bf9bccc1SDan Williams  *
768bf9bccc1SDan Williams  * Validate that a PMEM label, if present, aligns with the start of an
7693b6c6c03SDan Williams  * interleave set.
770bf9bccc1SDan Williams  */
nd_pmem_available_dpa(struct nd_region * nd_region,struct nd_mapping * nd_mapping)771bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
7723b6c6c03SDan Williams 				      struct nd_mapping *nd_mapping)
773bf9bccc1SDan Williams {
774bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
7753b6c6c03SDan Williams 	resource_size_t map_start, map_end, busy = 0;
776bf9bccc1SDan Williams 	struct resource *res;
7772522afb8SDan Williams 	unsigned long align;
778bf9bccc1SDan Williams 
779bf9bccc1SDan Williams 	if (!ndd)
780bf9bccc1SDan Williams 		return 0;
781bf9bccc1SDan Williams 
7822522afb8SDan Williams 	align = dpa_align(nd_region);
7832522afb8SDan Williams 	if (!align)
7842522afb8SDan Williams 		return 0;
7852522afb8SDan Williams 
786bf9bccc1SDan Williams 	map_start = nd_mapping->start;
787bf9bccc1SDan Williams 	map_end = map_start + nd_mapping->size - 1;
788a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
7892522afb8SDan Williams 		resource_size_t start, end;
7902522afb8SDan Williams 
7912522afb8SDan Williams 		start = ALIGN_DOWN(res->start, align);
7922522afb8SDan Williams 		end = ALIGN(res->end + 1, align) - 1;
7932522afb8SDan Williams 		if (start >= map_start && start < map_end) {
7943b6c6c03SDan Williams 			if (end > map_end) {
7953b6c6c03SDan Williams 				nd_dbg_dpa(nd_region, ndd, res,
7963b6c6c03SDan Williams 					   "misaligned to iset\n");
7973b6c6c03SDan Williams 				return 0;
7983b6c6c03SDan Williams 			}
7992522afb8SDan Williams 			busy += end - start + 1;
8002522afb8SDan Williams 		} else if (end >= map_start && end <= map_end) {
8012522afb8SDan Williams 			busy += end - start + 1;
8022522afb8SDan Williams 		} else if (map_start > start && map_start < end) {
803bf9bccc1SDan Williams 			/* total eclipse of the mapping */
804bf9bccc1SDan Williams 			busy += nd_mapping->size;
805bf9bccc1SDan Williams 		}
806a1f3e4d6SDan Williams 	}
807bf9bccc1SDan Williams 
8083b6c6c03SDan Williams 	if (busy < nd_mapping->size)
8093b6c6c03SDan Williams 		return ALIGN_DOWN(nd_mapping->size - busy, align);
810bf9bccc1SDan Williams 	return 0;
811bf9bccc1SDan Williams }
812bf9bccc1SDan Williams 
nvdimm_free_dpa(struct nvdimm_drvdata * ndd,struct resource * res)8134a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
8144a826c83SDan Williams {
8154a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8164a826c83SDan Williams 	kfree(res->name);
8174a826c83SDan Williams 	__release_region(&ndd->dpa, res->start, resource_size(res));
8184a826c83SDan Williams }
8194a826c83SDan Williams 
nvdimm_allocate_dpa(struct nvdimm_drvdata * ndd,struct nd_label_id * label_id,resource_size_t start,resource_size_t n)8204a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
8214a826c83SDan Williams 		struct nd_label_id *label_id, resource_size_t start,
8224a826c83SDan Williams 		resource_size_t n)
8234a826c83SDan Williams {
8244a826c83SDan Williams 	char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
8254a826c83SDan Williams 	struct resource *res;
8264a826c83SDan Williams 
8274a826c83SDan Williams 	if (!name)
8284a826c83SDan Williams 		return NULL;
8294a826c83SDan Williams 
8304a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8314a826c83SDan Williams 	res = __request_region(&ndd->dpa, start, n, name, 0);
8324a826c83SDan Williams 	if (!res)
8334a826c83SDan Williams 		kfree(name);
8344a826c83SDan Williams 	return res;
8354a826c83SDan Williams }
8364a826c83SDan Williams 
837bf9bccc1SDan Williams /**
838bf9bccc1SDan Williams  * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
839bf9bccc1SDan Williams  * @nvdimm: container of dpa-resource-root + labels
8403b6c6c03SDan Williams  * @label_id: dpa resource name of the form pmem-<human readable uuid>
841bf9bccc1SDan Williams  */
nvdimm_allocated_dpa(struct nvdimm_drvdata * ndd,struct nd_label_id * label_id)842bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
843bf9bccc1SDan Williams 		struct nd_label_id *label_id)
844bf9bccc1SDan Williams {
845bf9bccc1SDan Williams 	resource_size_t allocated = 0;
846bf9bccc1SDan Williams 	struct resource *res;
847bf9bccc1SDan Williams 
848bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
849bf9bccc1SDan Williams 		if (strcmp(res->name, label_id->id) == 0)
850bf9bccc1SDan Williams 			allocated += resource_size(res);
851bf9bccc1SDan Williams 
852bf9bccc1SDan Williams 	return allocated;
853bf9bccc1SDan Williams }
854bf9bccc1SDan Williams 
count_dimms(struct device * dev,void * c)8554d88a97aSDan Williams static int count_dimms(struct device *dev, void *c)
8564d88a97aSDan Williams {
8574d88a97aSDan Williams 	int *count = c;
8584d88a97aSDan Williams 
8594d88a97aSDan Williams 	if (is_nvdimm(dev))
8604d88a97aSDan Williams 		(*count)++;
8614d88a97aSDan Williams 	return 0;
8624d88a97aSDan Williams }
8634d88a97aSDan Williams 
nvdimm_bus_check_dimm_count(struct nvdimm_bus * nvdimm_bus,int dimm_count)8644d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
8654d88a97aSDan Williams {
8664d88a97aSDan Williams 	int count = 0;
8674d88a97aSDan Williams 	/* Flush any possible dimm registration failures */
8684d88a97aSDan Williams 	nd_synchronize();
8694d88a97aSDan Williams 
8704d88a97aSDan Williams 	device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
871426824d6SDan Williams 	dev_dbg(&nvdimm_bus->dev, "count: %d\n", count);
8724d88a97aSDan Williams 	if (count != dimm_count)
8734d88a97aSDan Williams 		return -ENXIO;
8744d88a97aSDan Williams 	return 0;
8754d88a97aSDan Williams }
8764d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
877b354aba0SDan Williams 
nvdimm_devs_exit(void)878b354aba0SDan Williams void __exit nvdimm_devs_exit(void)
879b354aba0SDan Williams {
880b354aba0SDan Williams 	ida_destroy(&dimm_ida);
881b354aba0SDan Williams }
882