xref: /openbmc/linux/drivers/nvdimm/dimm_devs.c (revision 81beea55)
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  */
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 
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  */
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
237bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd)
238bf9bccc1SDan Williams {
239bf9bccc1SDan Williams 	kref_get(&ndd->kref);
240bf9bccc1SDan Williams }
241bf9bccc1SDan Williams 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
344*81beea55SDan Williams 	device_lock(dev);
3457018c897SDan Williams 	rc = __available_slots_show(dev_get_drvdata(dev), buf);
346*81beea55SDan Williams 	device_unlock(dev);
3477018c897SDan Williams 
3487018c897SDan Williams 	return rc;
3497018c897SDan Williams }
3500ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots);
3510ba1c634SDan Williams 
3523c13e2acSDave Jiang __weak 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 
3577c02d53dSJane Chu 	if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
3587c02d53dSJane Chu 		return sprintf(buf, "overwrite\n");
359d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
360f2989396SDave Jiang 		return sprintf(buf, "disabled\n");
361d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
362f2989396SDave Jiang 		return sprintf(buf, "unlocked\n");
363d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
364f2989396SDave Jiang 		return sprintf(buf, "locked\n");
36589fa9d8eSDave Jiang 	return -ENOTTY;
366f2989396SDave Jiang }
367f2989396SDave Jiang 
368d78c620aSDan Williams static ssize_t frozen_show(struct device *dev,
369d78c620aSDan Williams 		struct device_attribute *attr, char *buf)
370d78c620aSDan Williams {
371d78c620aSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
372d78c620aSDan Williams 
373d78c620aSDan Williams 	return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
374d78c620aSDan Williams 				&nvdimm->sec.flags));
375f2989396SDave Jiang }
376d78c620aSDan Williams static DEVICE_ATTR_RO(frozen);
37737833fb7SDave Jiang 
37837833fb7SDave Jiang static ssize_t security_store(struct device *dev,
37937833fb7SDave Jiang 		struct device_attribute *attr, const char *buf, size_t len)
38037833fb7SDave Jiang 
38137833fb7SDave Jiang {
38237833fb7SDave Jiang 	ssize_t rc;
38337833fb7SDave Jiang 
38437833fb7SDave Jiang 	/*
38537833fb7SDave Jiang 	 * Require all userspace triggered security management to be
38637833fb7SDave Jiang 	 * done while probing is idle and the DIMM is not in active use
38737833fb7SDave Jiang 	 * in any region.
38837833fb7SDave Jiang 	 */
389*81beea55SDan Williams 	device_lock(dev);
39037833fb7SDave Jiang 	nvdimm_bus_lock(dev);
39137833fb7SDave Jiang 	wait_nvdimm_bus_probe_idle(dev);
3927b60422cSDan Williams 	rc = nvdimm_security_store(dev, buf, len);
39337833fb7SDave Jiang 	nvdimm_bus_unlock(dev);
394*81beea55SDan Williams 	device_unlock(dev);
39537833fb7SDave Jiang 
39637833fb7SDave Jiang 	return rc;
39737833fb7SDave Jiang }
39837833fb7SDave Jiang static DEVICE_ATTR_RW(security);
399f2989396SDave Jiang 
40062232e45SDan Williams static struct attribute *nvdimm_attributes[] = {
401eaf96153SDan Williams 	&dev_attr_state.attr,
402efbf6f50SDan Williams 	&dev_attr_flags.attr,
40362232e45SDan Williams 	&dev_attr_commands.attr,
4040ba1c634SDan Williams 	&dev_attr_available_slots.attr,
405f2989396SDave Jiang 	&dev_attr_security.attr,
406d78c620aSDan Williams 	&dev_attr_frozen.attr,
40762232e45SDan Williams 	NULL,
40862232e45SDan Williams };
40962232e45SDan Williams 
410f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
411f2989396SDave Jiang {
412f2989396SDave Jiang 	struct device *dev = container_of(kobj, typeof(*dev), kobj);
413f2989396SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
414f2989396SDave Jiang 
415d78c620aSDan Williams 	if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
416f2989396SDave Jiang 		return a->mode;
417d78c620aSDan Williams 	if (!nvdimm->sec.flags)
418f2989396SDave Jiang 		return 0;
419d78c620aSDan Williams 
420d78c620aSDan Williams 	if (a == &dev_attr_security.attr) {
421d78c620aSDan Williams 		/* Are there any state mutation ops (make writable)? */
422d2a4ac73SDave Jiang 		if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
42364e77c8cSDave Jiang 				|| nvdimm->sec.ops->change_key
4247d988097SDave Jiang 				|| nvdimm->sec.ops->erase
4257d988097SDave Jiang 				|| nvdimm->sec.ops->overwrite)
426f2989396SDave Jiang 			return a->mode;
42737833fb7SDave Jiang 		return 0444;
428f2989396SDave Jiang 	}
429f2989396SDave Jiang 
430d78c620aSDan Williams 	if (nvdimm->sec.ops->freeze)
431d78c620aSDan Williams 		return a->mode;
432d78c620aSDan Williams 	return 0;
433d78c620aSDan Williams }
434d78c620aSDan Williams 
435360eba7eSDan Williams static const struct attribute_group nvdimm_attribute_group = {
43662232e45SDan Williams 	.attrs = nvdimm_attributes,
437f2989396SDave Jiang 	.is_visible = nvdimm_visible,
43862232e45SDan Williams };
439360eba7eSDan Williams 
44048001ea5SDan Williams static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf)
44148001ea5SDan Williams {
44248001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
44348001ea5SDan Williams 	enum nvdimm_fwa_result result;
44448001ea5SDan Williams 
44548001ea5SDan Williams 	if (!nvdimm->fw_ops)
44648001ea5SDan Williams 		return -EOPNOTSUPP;
44748001ea5SDan Williams 
44848001ea5SDan Williams 	nvdimm_bus_lock(dev);
44948001ea5SDan Williams 	result = nvdimm->fw_ops->activate_result(nvdimm);
45048001ea5SDan Williams 	nvdimm_bus_unlock(dev);
45148001ea5SDan Williams 
45248001ea5SDan Williams 	switch (result) {
45348001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NONE:
45448001ea5SDan Williams 		return sprintf(buf, "none\n");
45548001ea5SDan Williams 	case NVDIMM_FWA_RESULT_SUCCESS:
45648001ea5SDan Williams 		return sprintf(buf, "success\n");
45748001ea5SDan Williams 	case NVDIMM_FWA_RESULT_FAIL:
45848001ea5SDan Williams 		return sprintf(buf, "fail\n");
45948001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NOTSTAGED:
46048001ea5SDan Williams 		return sprintf(buf, "not_staged\n");
46148001ea5SDan Williams 	case NVDIMM_FWA_RESULT_NEEDRESET:
46248001ea5SDan Williams 		return sprintf(buf, "need_reset\n");
46348001ea5SDan Williams 	default:
46448001ea5SDan Williams 		return -ENXIO;
46548001ea5SDan Williams 	}
46648001ea5SDan Williams }
46748001ea5SDan Williams static DEVICE_ATTR_ADMIN_RO(result);
46848001ea5SDan Williams 
46948001ea5SDan Williams static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf)
47048001ea5SDan Williams {
47148001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
47248001ea5SDan Williams 	enum nvdimm_fwa_state state;
47348001ea5SDan Williams 
47448001ea5SDan Williams 	if (!nvdimm->fw_ops)
47548001ea5SDan Williams 		return -EOPNOTSUPP;
47648001ea5SDan Williams 
47748001ea5SDan Williams 	nvdimm_bus_lock(dev);
47848001ea5SDan Williams 	state = nvdimm->fw_ops->activate_state(nvdimm);
47948001ea5SDan Williams 	nvdimm_bus_unlock(dev);
48048001ea5SDan Williams 
48148001ea5SDan Williams 	switch (state) {
48248001ea5SDan Williams 	case NVDIMM_FWA_IDLE:
48348001ea5SDan Williams 		return sprintf(buf, "idle\n");
48448001ea5SDan Williams 	case NVDIMM_FWA_BUSY:
48548001ea5SDan Williams 		return sprintf(buf, "busy\n");
48648001ea5SDan Williams 	case NVDIMM_FWA_ARMED:
48748001ea5SDan Williams 		return sprintf(buf, "armed\n");
48848001ea5SDan Williams 	default:
48948001ea5SDan Williams 		return -ENXIO;
49048001ea5SDan Williams 	}
49148001ea5SDan Williams }
49248001ea5SDan Williams 
49348001ea5SDan Williams static ssize_t activate_store(struct device *dev, struct device_attribute *attr,
49448001ea5SDan Williams 		const char *buf, size_t len)
49548001ea5SDan Williams {
49648001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
49748001ea5SDan Williams 	enum nvdimm_fwa_trigger arg;
49848001ea5SDan Williams 	int rc;
49948001ea5SDan Williams 
50048001ea5SDan Williams 	if (!nvdimm->fw_ops)
50148001ea5SDan Williams 		return -EOPNOTSUPP;
50248001ea5SDan Williams 
50348001ea5SDan Williams 	if (sysfs_streq(buf, "arm"))
50448001ea5SDan Williams 		arg = NVDIMM_FWA_ARM;
50548001ea5SDan Williams 	else if (sysfs_streq(buf, "disarm"))
50648001ea5SDan Williams 		arg = NVDIMM_FWA_DISARM;
50748001ea5SDan Williams 	else
50848001ea5SDan Williams 		return -EINVAL;
50948001ea5SDan Williams 
51048001ea5SDan Williams 	nvdimm_bus_lock(dev);
51148001ea5SDan Williams 	rc = nvdimm->fw_ops->arm(nvdimm, arg);
51248001ea5SDan Williams 	nvdimm_bus_unlock(dev);
51348001ea5SDan Williams 
51448001ea5SDan Williams 	if (rc < 0)
51548001ea5SDan Williams 		return rc;
51648001ea5SDan Williams 	return len;
51748001ea5SDan Williams }
51848001ea5SDan Williams static DEVICE_ATTR_ADMIN_RW(activate);
51948001ea5SDan Williams 
52048001ea5SDan Williams static struct attribute *nvdimm_firmware_attributes[] = {
52148001ea5SDan Williams 	&dev_attr_activate.attr,
52248001ea5SDan Williams 	&dev_attr_result.attr,
52362c78927SZqiang 	NULL,
52448001ea5SDan Williams };
52548001ea5SDan Williams 
52648001ea5SDan Williams static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
52748001ea5SDan Williams {
52848001ea5SDan Williams 	struct device *dev = container_of(kobj, typeof(*dev), kobj);
52948001ea5SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
53048001ea5SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
53148001ea5SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
53248001ea5SDan Williams 	enum nvdimm_fwa_capability cap;
53348001ea5SDan Williams 
53448001ea5SDan Williams 	if (!nd_desc->fw_ops)
53548001ea5SDan Williams 		return 0;
53648001ea5SDan Williams 	if (!nvdimm->fw_ops)
53748001ea5SDan Williams 		return 0;
53848001ea5SDan Williams 
53948001ea5SDan Williams 	nvdimm_bus_lock(dev);
54048001ea5SDan Williams 	cap = nd_desc->fw_ops->capability(nd_desc);
54148001ea5SDan Williams 	nvdimm_bus_unlock(dev);
54248001ea5SDan Williams 
54348001ea5SDan Williams 	if (cap < NVDIMM_FWA_CAP_QUIESCE)
54448001ea5SDan Williams 		return 0;
54548001ea5SDan Williams 
54648001ea5SDan Williams 	return a->mode;
54748001ea5SDan Williams }
54848001ea5SDan Williams 
54948001ea5SDan Williams static const struct attribute_group nvdimm_firmware_attribute_group = {
55048001ea5SDan Williams 	.name = "firmware",
55148001ea5SDan Williams 	.attrs = nvdimm_firmware_attributes,
55248001ea5SDan Williams 	.is_visible = nvdimm_firmware_visible,
55348001ea5SDan Williams };
55448001ea5SDan Williams 
555360eba7eSDan Williams static const struct attribute_group *nvdimm_attribute_groups[] = {
556360eba7eSDan Williams 	&nd_device_attribute_group,
557360eba7eSDan Williams 	&nvdimm_attribute_group,
55848001ea5SDan Williams 	&nvdimm_firmware_attribute_group,
559360eba7eSDan Williams 	NULL,
560360eba7eSDan Williams };
561360eba7eSDan Williams 
562360eba7eSDan Williams static const struct device_type nvdimm_device_type = {
563360eba7eSDan Williams 	.name = "nvdimm",
564360eba7eSDan Williams 	.release = nvdimm_release,
565360eba7eSDan Williams 	.groups = nvdimm_attribute_groups,
566360eba7eSDan Williams };
567360eba7eSDan Williams 
568360eba7eSDan Williams bool is_nvdimm(struct device *dev)
569360eba7eSDan Williams {
570360eba7eSDan Williams 	return dev->type == &nvdimm_device_type;
571360eba7eSDan Williams }
57262232e45SDan Williams 
5734a0079bcSDan Williams static struct lock_class_key nvdimm_key;
5744a0079bcSDan Williams 
575d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
576d6548ae4SDave Jiang 		void *provider_data, const struct attribute_group **groups,
577d6548ae4SDave Jiang 		unsigned long flags, unsigned long cmd_mask, int num_flush,
578f2989396SDave Jiang 		struct resource *flush_wpq, const char *dimm_id,
579a1facc1fSDan Williams 		const struct nvdimm_security_ops *sec_ops,
580a1facc1fSDan Williams 		const struct nvdimm_fw_ops *fw_ops)
581e6dfb2deSDan Williams {
582e6dfb2deSDan Williams 	struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
583e6dfb2deSDan Williams 	struct device *dev;
584e6dfb2deSDan Williams 
585e6dfb2deSDan Williams 	if (!nvdimm)
586e6dfb2deSDan Williams 		return NULL;
587e6dfb2deSDan Williams 
588e6dfb2deSDan Williams 	nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
589e6dfb2deSDan Williams 	if (nvdimm->id < 0) {
590e6dfb2deSDan Williams 		kfree(nvdimm);
591e6dfb2deSDan Williams 		return NULL;
592e6dfb2deSDan Williams 	}
593d6548ae4SDave Jiang 
594d6548ae4SDave Jiang 	nvdimm->dimm_id = dimm_id;
595e6dfb2deSDan Williams 	nvdimm->provider_data = provider_data;
596e6dfb2deSDan Williams 	nvdimm->flags = flags;
597e3654ecaSDan Williams 	nvdimm->cmd_mask = cmd_mask;
598e5ae3b25SDan Williams 	nvdimm->num_flush = num_flush;
599e5ae3b25SDan Williams 	nvdimm->flush_wpq = flush_wpq;
600eaf96153SDan Williams 	atomic_set(&nvdimm->busy, 0);
601e6dfb2deSDan Williams 	dev = &nvdimm->dev;
602e6dfb2deSDan Williams 	dev_set_name(dev, "nmem%d", nvdimm->id);
603e6dfb2deSDan Williams 	dev->parent = &nvdimm_bus->dev;
604e6dfb2deSDan Williams 	dev->type = &nvdimm_device_type;
60562232e45SDan Williams 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
606e6dfb2deSDan Williams 	dev->groups = groups;
607f2989396SDave Jiang 	nvdimm->sec.ops = sec_ops;
608a1facc1fSDan Williams 	nvdimm->fw_ops = fw_ops;
6097d988097SDave Jiang 	nvdimm->sec.overwrite_tmo = 0;
6107d988097SDave Jiang 	INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query);
611f2989396SDave Jiang 	/*
612f2989396SDave Jiang 	 * Security state must be initialized before device_add() for
613f2989396SDave Jiang 	 * attribute visibility.
614f2989396SDave Jiang 	 */
61589fa9d8eSDave Jiang 	/* get security state and extended (master) state */
616d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
617d78c620aSDan Williams 	nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
6184a0079bcSDan Williams 	device_initialize(dev);
6194a0079bcSDan Williams 	lockdep_set_class(&dev->mutex, &nvdimm_key);
6204d88a97aSDan Williams 	nd_device_register(dev);
621e6dfb2deSDan Williams 
622e6dfb2deSDan Williams 	return nvdimm;
623e6dfb2deSDan Williams }
624d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create);
6254d88a97aSDan Williams 
626fd14602dSDan Williams void nvdimm_delete(struct nvdimm *nvdimm)
627fd14602dSDan Williams {
628fd14602dSDan Williams 	struct device *dev = &nvdimm->dev;
629fd14602dSDan Williams 	bool dev_put = false;
630fd14602dSDan Williams 
631fd14602dSDan Williams 	/* We are shutting down. Make state frozen artificially. */
632fd14602dSDan Williams 	nvdimm_bus_lock(dev);
633fd14602dSDan Williams 	set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
634fd14602dSDan Williams 	if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
635fd14602dSDan Williams 		dev_put = true;
636fd14602dSDan Williams 	nvdimm_bus_unlock(dev);
637fd14602dSDan Williams 	cancel_delayed_work_sync(&nvdimm->dwork);
638fd14602dSDan Williams 	if (dev_put)
639fd14602dSDan Williams 		put_device(dev);
640fd14602dSDan Williams 	nd_device_unregister(dev, ND_SYNC);
641fd14602dSDan Williams }
642fd14602dSDan Williams EXPORT_SYMBOL_GPL(nvdimm_delete);
643fd14602dSDan Williams 
6441cd73865SDan Williams static void shutdown_security_notify(void *data)
6457d988097SDave Jiang {
6461cd73865SDan Williams 	struct nvdimm *nvdimm = data;
6471cd73865SDan Williams 
6481cd73865SDan Williams 	sysfs_put(nvdimm->sec.overwrite_state);
6491cd73865SDan Williams }
6501cd73865SDan Williams 
6511cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev)
6521cd73865SDan Williams {
6531cd73865SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
6541cd73865SDan Williams 
655d78c620aSDan Williams 	if (!nvdimm->sec.flags || !nvdimm->sec.ops
6561cd73865SDan Williams 			|| !nvdimm->sec.ops->overwrite)
6577d988097SDave Jiang 		return 0;
6581cd73865SDan Williams 	nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
6591cd73865SDan Williams 	if (!nvdimm->sec.overwrite_state)
6601cd73865SDan Williams 		return -ENOMEM;
6611cd73865SDan Williams 
6621cd73865SDan Williams 	return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm);
6637d988097SDave Jiang }
6647d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events);
6657d988097SDave Jiang 
6667d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm)
6677d988097SDave Jiang {
6687d988097SDave Jiang 	return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
6697d988097SDave Jiang }
6707d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite);
6717d988097SDave Jiang 
67237833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm)
67337833fb7SDave Jiang {
67437833fb7SDave Jiang 	int rc;
67537833fb7SDave Jiang 
67637833fb7SDave Jiang 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
67737833fb7SDave Jiang 
67837833fb7SDave Jiang 	if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
67937833fb7SDave Jiang 		return -EOPNOTSUPP;
68037833fb7SDave Jiang 
681d78c620aSDan Williams 	if (!nvdimm->sec.flags)
68237833fb7SDave Jiang 		return -EIO;
68337833fb7SDave Jiang 
6847d988097SDave Jiang 	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
6857d988097SDave Jiang 		dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n");
6867d988097SDave Jiang 		return -EBUSY;
6877d988097SDave Jiang 	}
6887d988097SDave Jiang 
68937833fb7SDave Jiang 	rc = nvdimm->sec.ops->freeze(nvdimm);
690d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
69137833fb7SDave Jiang 
69237833fb7SDave Jiang 	return rc;
69337833fb7SDave Jiang }
69437833fb7SDave Jiang 
6952522afb8SDan Williams static unsigned long dpa_align(struct nd_region *nd_region)
6962522afb8SDan Williams {
6972522afb8SDan Williams 	struct device *dev = &nd_region->dev;
6982522afb8SDan Williams 
6992522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev),
7002522afb8SDan Williams 				"bus lock required for capacity provision\n"))
7012522afb8SDan Williams 		return 0;
7022522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align
7032522afb8SDan Williams 				% nd_region->ndr_mappings,
7042522afb8SDan Williams 				"invalid region align %#lx mappings: %d\n",
7052522afb8SDan Williams 				nd_region->align, nd_region->ndr_mappings))
7062522afb8SDan Williams 		return 0;
7072522afb8SDan Williams 	return nd_region->align / nd_region->ndr_mappings;
7082522afb8SDan Williams }
7092522afb8SDan Williams 
7101b40e09aSDan Williams /**
71112e3129eSKeith Busch  * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max
71212e3129eSKeith Busch  *			   contiguous unallocated dpa range.
71312e3129eSKeith Busch  * @nd_region: constrain available space check to this reference region
71412e3129eSKeith Busch  * @nd_mapping: container of dpa-resource-root + labels
71512e3129eSKeith Busch  */
71612e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
71712e3129eSKeith Busch 					   struct nd_mapping *nd_mapping)
71812e3129eSKeith Busch {
71912e3129eSKeith Busch 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
72012e3129eSKeith Busch 	struct nvdimm_bus *nvdimm_bus;
72112e3129eSKeith Busch 	resource_size_t max = 0;
72212e3129eSKeith Busch 	struct resource *res;
7232522afb8SDan Williams 	unsigned long align;
72412e3129eSKeith Busch 
72512e3129eSKeith Busch 	/* if a dimm is disabled the available capacity is zero */
72612e3129eSKeith Busch 	if (!ndd)
72712e3129eSKeith Busch 		return 0;
72812e3129eSKeith Busch 
7292522afb8SDan Williams 	align = dpa_align(nd_region);
7302522afb8SDan Williams 	if (!align)
7312522afb8SDan Williams 		return 0;
7322522afb8SDan Williams 
73312e3129eSKeith Busch 	nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
73412e3129eSKeith Busch 	if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm))
73512e3129eSKeith Busch 		return 0;
73612e3129eSKeith Busch 	for_each_dpa_resource(ndd, res) {
7372522afb8SDan Williams 		resource_size_t start, end;
7382522afb8SDan Williams 
73912e3129eSKeith Busch 		if (strcmp(res->name, "pmem-reserve") != 0)
74012e3129eSKeith Busch 			continue;
7412522afb8SDan Williams 		/* trim free space relative to current alignment setting */
7422522afb8SDan Williams 		start = ALIGN(res->start, align);
7432522afb8SDan Williams 		end = ALIGN_DOWN(res->end + 1, align) - 1;
7442522afb8SDan Williams 		if (end < start)
7452522afb8SDan Williams 			continue;
7462522afb8SDan Williams 		if (end - start + 1 > max)
7472522afb8SDan Williams 			max = end - start + 1;
74812e3129eSKeith Busch 	}
74912e3129eSKeith Busch 	release_free_pmem(nvdimm_bus, nd_mapping);
75012e3129eSKeith Busch 	return max;
75112e3129eSKeith Busch }
75212e3129eSKeith Busch 
75312e3129eSKeith Busch /**
754bf9bccc1SDan Williams  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
755bf9bccc1SDan Williams  * @nd_mapping: container of dpa-resource-root + labels
756bf9bccc1SDan Williams  * @nd_region: constrain available space check to this reference region
757bf9bccc1SDan Williams  *
758bf9bccc1SDan Williams  * Validate that a PMEM label, if present, aligns with the start of an
7593b6c6c03SDan Williams  * interleave set.
760bf9bccc1SDan Williams  */
761bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
7623b6c6c03SDan Williams 				      struct nd_mapping *nd_mapping)
763bf9bccc1SDan Williams {
764bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
7653b6c6c03SDan Williams 	resource_size_t map_start, map_end, busy = 0;
766bf9bccc1SDan Williams 	struct resource *res;
7672522afb8SDan Williams 	unsigned long align;
768bf9bccc1SDan Williams 
769bf9bccc1SDan Williams 	if (!ndd)
770bf9bccc1SDan Williams 		return 0;
771bf9bccc1SDan Williams 
7722522afb8SDan Williams 	align = dpa_align(nd_region);
7732522afb8SDan Williams 	if (!align)
7742522afb8SDan Williams 		return 0;
7752522afb8SDan Williams 
776bf9bccc1SDan Williams 	map_start = nd_mapping->start;
777bf9bccc1SDan Williams 	map_end = map_start + nd_mapping->size - 1;
778a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
7792522afb8SDan Williams 		resource_size_t start, end;
7802522afb8SDan Williams 
7812522afb8SDan Williams 		start = ALIGN_DOWN(res->start, align);
7822522afb8SDan Williams 		end = ALIGN(res->end + 1, align) - 1;
7832522afb8SDan Williams 		if (start >= map_start && start < map_end) {
7843b6c6c03SDan Williams 			if (end > map_end) {
7853b6c6c03SDan Williams 				nd_dbg_dpa(nd_region, ndd, res,
7863b6c6c03SDan Williams 					   "misaligned to iset\n");
7873b6c6c03SDan Williams 				return 0;
7883b6c6c03SDan Williams 			}
7892522afb8SDan Williams 			busy += end - start + 1;
7902522afb8SDan Williams 		} else if (end >= map_start && end <= map_end) {
7912522afb8SDan Williams 			busy += end - start + 1;
7922522afb8SDan Williams 		} else if (map_start > start && map_start < end) {
793bf9bccc1SDan Williams 			/* total eclipse of the mapping */
794bf9bccc1SDan Williams 			busy += nd_mapping->size;
795bf9bccc1SDan Williams 		}
796a1f3e4d6SDan Williams 	}
797bf9bccc1SDan Williams 
7983b6c6c03SDan Williams 	if (busy < nd_mapping->size)
7993b6c6c03SDan Williams 		return ALIGN_DOWN(nd_mapping->size - busy, align);
800bf9bccc1SDan Williams 	return 0;
801bf9bccc1SDan Williams }
802bf9bccc1SDan Williams 
8034a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
8044a826c83SDan Williams {
8054a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8064a826c83SDan Williams 	kfree(res->name);
8074a826c83SDan Williams 	__release_region(&ndd->dpa, res->start, resource_size(res));
8084a826c83SDan Williams }
8094a826c83SDan Williams 
8104a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
8114a826c83SDan Williams 		struct nd_label_id *label_id, resource_size_t start,
8124a826c83SDan Williams 		resource_size_t n)
8134a826c83SDan Williams {
8144a826c83SDan Williams 	char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
8154a826c83SDan Williams 	struct resource *res;
8164a826c83SDan Williams 
8174a826c83SDan Williams 	if (!name)
8184a826c83SDan Williams 		return NULL;
8194a826c83SDan Williams 
8204a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8214a826c83SDan Williams 	res = __request_region(&ndd->dpa, start, n, name, 0);
8224a826c83SDan Williams 	if (!res)
8234a826c83SDan Williams 		kfree(name);
8244a826c83SDan Williams 	return res;
8254a826c83SDan Williams }
8264a826c83SDan Williams 
827bf9bccc1SDan Williams /**
828bf9bccc1SDan Williams  * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
829bf9bccc1SDan Williams  * @nvdimm: container of dpa-resource-root + labels
8303b6c6c03SDan Williams  * @label_id: dpa resource name of the form pmem-<human readable uuid>
831bf9bccc1SDan Williams  */
832bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
833bf9bccc1SDan Williams 		struct nd_label_id *label_id)
834bf9bccc1SDan Williams {
835bf9bccc1SDan Williams 	resource_size_t allocated = 0;
836bf9bccc1SDan Williams 	struct resource *res;
837bf9bccc1SDan Williams 
838bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
839bf9bccc1SDan Williams 		if (strcmp(res->name, label_id->id) == 0)
840bf9bccc1SDan Williams 			allocated += resource_size(res);
841bf9bccc1SDan Williams 
842bf9bccc1SDan Williams 	return allocated;
843bf9bccc1SDan Williams }
844bf9bccc1SDan Williams 
8454d88a97aSDan Williams static int count_dimms(struct device *dev, void *c)
8464d88a97aSDan Williams {
8474d88a97aSDan Williams 	int *count = c;
8484d88a97aSDan Williams 
8494d88a97aSDan Williams 	if (is_nvdimm(dev))
8504d88a97aSDan Williams 		(*count)++;
8514d88a97aSDan Williams 	return 0;
8524d88a97aSDan Williams }
8534d88a97aSDan Williams 
8544d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
8554d88a97aSDan Williams {
8564d88a97aSDan Williams 	int count = 0;
8574d88a97aSDan Williams 	/* Flush any possible dimm registration failures */
8584d88a97aSDan Williams 	nd_synchronize();
8594d88a97aSDan Williams 
8604d88a97aSDan Williams 	device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
861426824d6SDan Williams 	dev_dbg(&nvdimm_bus->dev, "count: %d\n", count);
8624d88a97aSDan Williams 	if (count != dimm_count)
8634d88a97aSDan Williams 		return -ENXIO;
8644d88a97aSDan Williams 	return 0;
8654d88a97aSDan Williams }
8664d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
867b354aba0SDan Williams 
868b354aba0SDan Williams void __exit nvdimm_devs_exit(void)
869b354aba0SDan Williams {
870b354aba0SDan Williams 	ida_destroy(&dimm_ida);
871b354aba0SDan Williams }
872