xref: /openbmc/linux/drivers/nvdimm/dimm_devs.c (revision 2d657d17)
1e6dfb2deSDan Williams /*
2e6dfb2deSDan Williams  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3e6dfb2deSDan Williams  *
4e6dfb2deSDan Williams  * This program is free software; you can redistribute it and/or modify
5e6dfb2deSDan Williams  * it under the terms of version 2 of the GNU General Public License as
6e6dfb2deSDan Williams  * published by the Free Software Foundation.
7e6dfb2deSDan Williams  *
8e6dfb2deSDan Williams  * This program is distributed in the hope that it will be useful, but
9e6dfb2deSDan Williams  * WITHOUT ANY WARRANTY; without even the implied warranty of
10e6dfb2deSDan Williams  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11e6dfb2deSDan Williams  * General Public License for more details.
12e6dfb2deSDan Williams  */
13e6dfb2deSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
144d88a97aSDan Williams #include <linux/vmalloc.h>
15e6dfb2deSDan Williams #include <linux/device.h>
1662232e45SDan Williams #include <linux/ndctl.h>
17e6dfb2deSDan Williams #include <linux/slab.h>
18e6dfb2deSDan Williams #include <linux/io.h>
19e6dfb2deSDan Williams #include <linux/fs.h>
20e6dfb2deSDan Williams #include <linux/mm.h>
21e6dfb2deSDan Williams #include "nd-core.h"
220ba1c634SDan Williams #include "label.h"
23ca6a4657SDan Williams #include "pmem.h"
244d88a97aSDan Williams #include "nd.h"
25e6dfb2deSDan Williams 
26e6dfb2deSDan Williams static DEFINE_IDA(dimm_ida);
27e6dfb2deSDan Williams 
284d88a97aSDan Williams /*
294d88a97aSDan Williams  * Retrieve bus and dimm handle and return if this bus supports
304d88a97aSDan Williams  * get_config_data commands
314d88a97aSDan Williams  */
32aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev)
334d88a97aSDan Williams {
34aee65987SToshi Kani 	struct nvdimm *nvdimm = to_nvdimm(dev);
354d88a97aSDan Williams 
36aee65987SToshi Kani 	if (!nvdimm->cmd_mask ||
37aee65987SToshi Kani 	    !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
388f078b38SDan Williams 		if (test_bit(NDD_ALIASING, &nvdimm->flags))
394d88a97aSDan Williams 			return -ENXIO;
40aee65987SToshi Kani 		else
41aee65987SToshi Kani 			return -ENOTTY;
42aee65987SToshi Kani 	}
434d88a97aSDan Williams 
444d88a97aSDan Williams 	return 0;
454d88a97aSDan Williams }
464d88a97aSDan Williams 
474d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd)
484d88a97aSDan Williams {
49aee65987SToshi Kani 	int rc;
504d88a97aSDan Williams 
51aee65987SToshi Kani 	if (!ndd)
52aee65987SToshi Kani 		return -EINVAL;
53aee65987SToshi Kani 
54aee65987SToshi Kani 	rc = nvdimm_check_config_data(ndd->dev);
55aee65987SToshi Kani 	if (rc)
564d88a97aSDan Williams 		dev_dbg(ndd->dev, "%pf: %s error: %d\n",
574d88a97aSDan Williams 				__builtin_return_address(0), __func__, rc);
584d88a97aSDan Williams 	return rc;
594d88a97aSDan Williams }
604d88a97aSDan Williams 
614d88a97aSDan Williams /**
624d88a97aSDan Williams  * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
634d88a97aSDan Williams  * @nvdimm: dimm to initialize
644d88a97aSDan Williams  */
654d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
664d88a97aSDan Williams {
674d88a97aSDan Williams 	struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
684d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
694d88a97aSDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
704d88a97aSDan Williams 	int rc = validate_dimm(ndd);
719d62ed96SDan Williams 	int cmd_rc = 0;
724d88a97aSDan Williams 
734d88a97aSDan Williams 	if (rc)
744d88a97aSDan Williams 		return rc;
754d88a97aSDan Williams 
764d88a97aSDan Williams 	if (cmd->config_size)
774d88a97aSDan Williams 		return 0; /* already valid */
784d88a97aSDan Williams 
794d88a97aSDan Williams 	memset(cmd, 0, sizeof(*cmd));
804d88a97aSDan Williams 	nd_desc = nvdimm_bus->nd_desc;
819d62ed96SDan Williams 	rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
829d62ed96SDan Williams 			ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc);
839d62ed96SDan Williams 	if (rc < 0)
849d62ed96SDan Williams 		return rc;
859d62ed96SDan Williams 	return cmd_rc;
864d88a97aSDan Williams }
874d88a97aSDan Williams 
882d657d17SAlexander Duyck int nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf,
892d657d17SAlexander Duyck 			   size_t offset, size_t len)
904d88a97aSDan Williams {
914d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
922d657d17SAlexander Duyck 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
93e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
944d88a97aSDan Williams 	struct nd_cmd_get_config_data_hdr *cmd;
952d657d17SAlexander Duyck 	size_t max_cmd_size, buf_offset;
964d88a97aSDan Williams 
974d88a97aSDan Williams 	if (rc)
984d88a97aSDan Williams 		return rc;
994d88a97aSDan Williams 
1002d657d17SAlexander Duyck 	if (offset + len > ndd->nsarea.config_size)
1014d88a97aSDan Williams 		return -ENXIO;
1024d88a97aSDan Williams 
1032d657d17SAlexander Duyck 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
104d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
1054d88a97aSDan Williams 	if (!cmd)
1064d88a97aSDan Williams 		return -ENOMEM;
1074d88a97aSDan Williams 
1082d657d17SAlexander Duyck 	for (buf_offset = 0; len;
1092d657d17SAlexander Duyck 	     len -= cmd->in_length, buf_offset += cmd->in_length) {
1102d657d17SAlexander Duyck 		size_t cmd_size;
1112d657d17SAlexander Duyck 
1122d657d17SAlexander Duyck 		cmd->in_offset = offset + buf_offset;
1132d657d17SAlexander Duyck 		cmd->in_length = min(max_cmd_size, len);
1142d657d17SAlexander Duyck 
1152d657d17SAlexander Duyck 		cmd_size = sizeof(*cmd) + cmd->in_length;
1162d657d17SAlexander Duyck 
1174d88a97aSDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
1182d657d17SAlexander Duyck 				ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
119e7c5a571SDan Williams 		if (rc < 0)
120e7c5a571SDan Williams 			break;
121e7c5a571SDan Williams 		if (cmd_rc < 0) {
122e7c5a571SDan Williams 			rc = cmd_rc;
1234d88a97aSDan Williams 			break;
1244d88a97aSDan Williams 		}
1252d657d17SAlexander Duyck 
1262d657d17SAlexander Duyck 		/* out_buf should be valid, copy it into our output buffer */
1272d657d17SAlexander Duyck 		memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length);
1284d88a97aSDan Williams 	}
129d11cf4a7SDan Williams 	kvfree(cmd);
1304d88a97aSDan Williams 
1314d88a97aSDan Williams 	return rc;
1324d88a97aSDan Williams }
1334d88a97aSDan Williams 
134f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
135f524bf27SDan Williams 		void *buf, size_t len)
136f524bf27SDan Williams {
137f524bf27SDan Williams 	size_t max_cmd_size, buf_offset;
138f524bf27SDan Williams 	struct nd_cmd_set_config_hdr *cmd;
139e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
140f524bf27SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
141f524bf27SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
142f524bf27SDan Williams 
143f524bf27SDan Williams 	if (rc)
144f524bf27SDan Williams 		return rc;
145f524bf27SDan Williams 
146f524bf27SDan Williams 	if (offset + len > ndd->nsarea.config_size)
147f524bf27SDan Williams 		return -ENXIO;
148f524bf27SDan Williams 
149d11cf4a7SDan Williams 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
150d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
151f524bf27SDan Williams 	if (!cmd)
152f524bf27SDan Williams 		return -ENOMEM;
153f524bf27SDan Williams 
154f524bf27SDan Williams 	for (buf_offset = 0; len; len -= cmd->in_length,
155f524bf27SDan Williams 			buf_offset += cmd->in_length) {
156f524bf27SDan Williams 		size_t cmd_size;
157f524bf27SDan Williams 
158f524bf27SDan Williams 		cmd->in_offset = offset + buf_offset;
159f524bf27SDan Williams 		cmd->in_length = min(max_cmd_size, len);
160f524bf27SDan Williams 		memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
161f524bf27SDan Williams 
162f524bf27SDan Williams 		/* status is output in the last 4-bytes of the command buffer */
163f524bf27SDan Williams 		cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
164f524bf27SDan Williams 
165f524bf27SDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
166e7c5a571SDan Williams 				ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
167e7c5a571SDan Williams 		if (rc < 0)
168e7c5a571SDan Williams 			break;
169e7c5a571SDan Williams 		if (cmd_rc < 0) {
170e7c5a571SDan Williams 			rc = cmd_rc;
171f524bf27SDan Williams 			break;
172f524bf27SDan Williams 		}
173f524bf27SDan Williams 	}
174d11cf4a7SDan Williams 	kvfree(cmd);
175f524bf27SDan Williams 
176f524bf27SDan Williams 	return rc;
177f524bf27SDan Williams }
178f524bf27SDan Williams 
17942237e39SDan Williams void nvdimm_set_aliasing(struct device *dev)
18042237e39SDan Williams {
18142237e39SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
18242237e39SDan Williams 
1838f078b38SDan Williams 	set_bit(NDD_ALIASING, &nvdimm->flags);
1848f078b38SDan Williams }
1858f078b38SDan Williams 
1868f078b38SDan Williams void nvdimm_set_locked(struct device *dev)
1878f078b38SDan Williams {
1888f078b38SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1898f078b38SDan Williams 
1908f078b38SDan Williams 	set_bit(NDD_LOCKED, &nvdimm->flags);
19142237e39SDan Williams }
19242237e39SDan Williams 
193d34cb808SDan Williams void nvdimm_clear_locked(struct device *dev)
194d34cb808SDan Williams {
195d34cb808SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
196d34cb808SDan Williams 
197d34cb808SDan Williams 	clear_bit(NDD_LOCKED, &nvdimm->flags);
198d34cb808SDan Williams }
199d34cb808SDan Williams 
200e6dfb2deSDan Williams static void nvdimm_release(struct device *dev)
201e6dfb2deSDan Williams {
202e6dfb2deSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
203e6dfb2deSDan Williams 
204e6dfb2deSDan Williams 	ida_simple_remove(&dimm_ida, nvdimm->id);
205e6dfb2deSDan Williams 	kfree(nvdimm);
206e6dfb2deSDan Williams }
207e6dfb2deSDan Williams 
208e6dfb2deSDan Williams static struct device_type nvdimm_device_type = {
209e6dfb2deSDan Williams 	.name = "nvdimm",
210e6dfb2deSDan Williams 	.release = nvdimm_release,
211e6dfb2deSDan Williams };
212e6dfb2deSDan Williams 
21362232e45SDan Williams bool is_nvdimm(struct device *dev)
214e6dfb2deSDan Williams {
215e6dfb2deSDan Williams 	return dev->type == &nvdimm_device_type;
216e6dfb2deSDan Williams }
217e6dfb2deSDan Williams 
218e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev)
219e6dfb2deSDan Williams {
220e6dfb2deSDan Williams 	struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
221e6dfb2deSDan Williams 
222e6dfb2deSDan Williams 	WARN_ON(!is_nvdimm(dev));
223e6dfb2deSDan Williams 	return nvdimm;
224e6dfb2deSDan Williams }
225e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm);
226e6dfb2deSDan Williams 
227047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr)
228047fc8a1SRoss Zwisler {
229047fc8a1SRoss Zwisler 	struct nd_region *nd_region = &ndbr->nd_region;
230047fc8a1SRoss Zwisler 	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
231047fc8a1SRoss Zwisler 
232047fc8a1SRoss Zwisler 	return nd_mapping->nvdimm;
233047fc8a1SRoss Zwisler }
234047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm);
235047fc8a1SRoss Zwisler 
236ca6a4657SDan Williams unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr)
237ca6a4657SDan Williams {
238ca6a4657SDan Williams 	/* pmem mapping properties are private to libnvdimm */
239ca6a4657SDan Williams 	return ARCH_MEMREMAP_PMEM;
240ca6a4657SDan Williams }
241ca6a4657SDan Williams EXPORT_SYMBOL_GPL(nd_blk_memremap_flags);
242ca6a4657SDan Williams 
243bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
244bf9bccc1SDan Williams {
245bf9bccc1SDan Williams 	struct nvdimm *nvdimm = nd_mapping->nvdimm;
246bf9bccc1SDan Williams 
247bf9bccc1SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
248bf9bccc1SDan Williams 
249bf9bccc1SDan Williams 	return dev_get_drvdata(&nvdimm->dev);
250bf9bccc1SDan Williams }
251bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd);
252bf9bccc1SDan Williams 
253bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref)
254bf9bccc1SDan Williams {
255bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
256bf9bccc1SDan Williams 	struct device *dev = ndd->dev;
257bf9bccc1SDan Williams 	struct resource *res, *_r;
258bf9bccc1SDan Williams 
259426824d6SDan Williams 	dev_dbg(dev, "trace\n");
260bf9bccc1SDan Williams 	nvdimm_bus_lock(dev);
261bf9bccc1SDan Williams 	for_each_dpa_resource_safe(ndd, res, _r)
262bf9bccc1SDan Williams 		nvdimm_free_dpa(ndd, res);
263bf9bccc1SDan Williams 	nvdimm_bus_unlock(dev);
264bf9bccc1SDan Williams 
265a06a7576Syalin wang 	kvfree(ndd->data);
266bf9bccc1SDan Williams 	kfree(ndd);
267bf9bccc1SDan Williams 	put_device(dev);
268bf9bccc1SDan Williams }
269bf9bccc1SDan Williams 
270bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd)
271bf9bccc1SDan Williams {
272bf9bccc1SDan Williams 	kref_get(&ndd->kref);
273bf9bccc1SDan Williams }
274bf9bccc1SDan Williams 
275bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd)
276bf9bccc1SDan Williams {
277bf9bccc1SDan Williams 	if (ndd)
278bf9bccc1SDan Williams 		kref_put(&ndd->kref, nvdimm_drvdata_release);
279bf9bccc1SDan Williams }
280bf9bccc1SDan Williams 
281e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm)
282e6dfb2deSDan Williams {
283e6dfb2deSDan Williams 	return dev_name(&nvdimm->dev);
284e6dfb2deSDan Williams }
285e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name);
286e6dfb2deSDan Williams 
287ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
288ba9c8dd3SDan Williams {
289ba9c8dd3SDan Williams 	return &nvdimm->dev.kobj;
290ba9c8dd3SDan Williams }
291ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj);
292ba9c8dd3SDan Williams 
293e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
294e3654ecaSDan Williams {
295e3654ecaSDan Williams 	return nvdimm->cmd_mask;
296e3654ecaSDan Williams }
297e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
298e3654ecaSDan Williams 
299e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm)
300e6dfb2deSDan Williams {
30162232e45SDan Williams 	if (nvdimm)
302e6dfb2deSDan Williams 		return nvdimm->provider_data;
30362232e45SDan Williams 	return NULL;
304e6dfb2deSDan Williams }
305e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data);
306e6dfb2deSDan Williams 
30762232e45SDan Williams static ssize_t commands_show(struct device *dev,
30862232e45SDan Williams 		struct device_attribute *attr, char *buf)
30962232e45SDan Williams {
31062232e45SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
31162232e45SDan Williams 	int cmd, len = 0;
31262232e45SDan Williams 
313e3654ecaSDan Williams 	if (!nvdimm->cmd_mask)
31462232e45SDan Williams 		return sprintf(buf, "\n");
31562232e45SDan Williams 
316e3654ecaSDan Williams 	for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
31762232e45SDan Williams 		len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
31862232e45SDan Williams 	len += sprintf(buf + len, "\n");
31962232e45SDan Williams 	return len;
32062232e45SDan Williams }
32162232e45SDan Williams static DEVICE_ATTR_RO(commands);
32262232e45SDan Williams 
323efbf6f50SDan Williams static ssize_t flags_show(struct device *dev,
324efbf6f50SDan Williams 		struct device_attribute *attr, char *buf)
325efbf6f50SDan Williams {
326efbf6f50SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
327efbf6f50SDan Williams 
328efbf6f50SDan Williams 	return sprintf(buf, "%s%s\n",
329efbf6f50SDan Williams 			test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "",
330efbf6f50SDan Williams 			test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : "");
331efbf6f50SDan Williams }
332efbf6f50SDan Williams static DEVICE_ATTR_RO(flags);
333efbf6f50SDan Williams 
334eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr,
335eaf96153SDan Williams 		char *buf)
336eaf96153SDan Williams {
337eaf96153SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
338eaf96153SDan Williams 
339eaf96153SDan Williams 	/*
340eaf96153SDan Williams 	 * The state may be in the process of changing, userspace should
341eaf96153SDan Williams 	 * quiesce probing if it wants a static answer
342eaf96153SDan Williams 	 */
343eaf96153SDan Williams 	nvdimm_bus_lock(dev);
344eaf96153SDan Williams 	nvdimm_bus_unlock(dev);
345eaf96153SDan Williams 	return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy)
346eaf96153SDan Williams 			? "active" : "idle");
347eaf96153SDan Williams }
348eaf96153SDan Williams static DEVICE_ATTR_RO(state);
349eaf96153SDan Williams 
3500ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev,
3510ba1c634SDan Williams 		struct device_attribute *attr, char *buf)
3520ba1c634SDan Williams {
3530ba1c634SDan Williams 	struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
3540ba1c634SDan Williams 	ssize_t rc;
3550ba1c634SDan Williams 	u32 nfree;
3560ba1c634SDan Williams 
3570ba1c634SDan Williams 	if (!ndd)
3580ba1c634SDan Williams 		return -ENXIO;
3590ba1c634SDan Williams 
3600ba1c634SDan Williams 	nvdimm_bus_lock(dev);
3610ba1c634SDan Williams 	nfree = nd_label_nfree(ndd);
3620ba1c634SDan Williams 	if (nfree - 1 > nfree) {
3630ba1c634SDan Williams 		dev_WARN_ONCE(dev, 1, "we ate our last label?\n");
3640ba1c634SDan Williams 		nfree = 0;
3650ba1c634SDan Williams 	} else
3660ba1c634SDan Williams 		nfree--;
3670ba1c634SDan Williams 	rc = sprintf(buf, "%d\n", nfree);
3680ba1c634SDan Williams 	nvdimm_bus_unlock(dev);
3690ba1c634SDan Williams 	return rc;
3700ba1c634SDan Williams }
3710ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots);
3720ba1c634SDan Williams 
37362232e45SDan Williams static struct attribute *nvdimm_attributes[] = {
374eaf96153SDan Williams 	&dev_attr_state.attr,
375efbf6f50SDan Williams 	&dev_attr_flags.attr,
37662232e45SDan Williams 	&dev_attr_commands.attr,
3770ba1c634SDan Williams 	&dev_attr_available_slots.attr,
37862232e45SDan Williams 	NULL,
37962232e45SDan Williams };
38062232e45SDan Williams 
38162232e45SDan Williams struct attribute_group nvdimm_attribute_group = {
38262232e45SDan Williams 	.attrs = nvdimm_attributes,
38362232e45SDan Williams };
38462232e45SDan Williams EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
38562232e45SDan Williams 
386e6dfb2deSDan Williams struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
38762232e45SDan Williams 		const struct attribute_group **groups, unsigned long flags,
388e5ae3b25SDan Williams 		unsigned long cmd_mask, int num_flush,
389e5ae3b25SDan Williams 		struct resource *flush_wpq)
390e6dfb2deSDan Williams {
391e6dfb2deSDan Williams 	struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
392e6dfb2deSDan Williams 	struct device *dev;
393e6dfb2deSDan Williams 
394e6dfb2deSDan Williams 	if (!nvdimm)
395e6dfb2deSDan Williams 		return NULL;
396e6dfb2deSDan Williams 
397e6dfb2deSDan Williams 	nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
398e6dfb2deSDan Williams 	if (nvdimm->id < 0) {
399e6dfb2deSDan Williams 		kfree(nvdimm);
400e6dfb2deSDan Williams 		return NULL;
401e6dfb2deSDan Williams 	}
402e6dfb2deSDan Williams 	nvdimm->provider_data = provider_data;
403e6dfb2deSDan Williams 	nvdimm->flags = flags;
404e3654ecaSDan Williams 	nvdimm->cmd_mask = cmd_mask;
405e5ae3b25SDan Williams 	nvdimm->num_flush = num_flush;
406e5ae3b25SDan Williams 	nvdimm->flush_wpq = flush_wpq;
407eaf96153SDan Williams 	atomic_set(&nvdimm->busy, 0);
408e6dfb2deSDan Williams 	dev = &nvdimm->dev;
409e6dfb2deSDan Williams 	dev_set_name(dev, "nmem%d", nvdimm->id);
410e6dfb2deSDan Williams 	dev->parent = &nvdimm_bus->dev;
411e6dfb2deSDan Williams 	dev->type = &nvdimm_device_type;
41262232e45SDan Williams 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
413e6dfb2deSDan Williams 	dev->groups = groups;
4144d88a97aSDan Williams 	nd_device_register(dev);
415e6dfb2deSDan Williams 
416e6dfb2deSDan Williams 	return nvdimm;
417e6dfb2deSDan Williams }
418e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_create);
4194d88a97aSDan Williams 
420762d067dSDan Williams int alias_dpa_busy(struct device *dev, void *data)
421a1f3e4d6SDan Williams {
422fe514739SDan Williams 	resource_size_t map_end, blk_start, new;
423a1f3e4d6SDan Williams 	struct blk_alloc_info *info = data;
424a1f3e4d6SDan Williams 	struct nd_mapping *nd_mapping;
425a1f3e4d6SDan Williams 	struct nd_region *nd_region;
426a1f3e4d6SDan Williams 	struct nvdimm_drvdata *ndd;
427a1f3e4d6SDan Williams 	struct resource *res;
428a1f3e4d6SDan Williams 	int i;
429a1f3e4d6SDan Williams 
430c9e582aaSDan Williams 	if (!is_memory(dev))
431a1f3e4d6SDan Williams 		return 0;
432a1f3e4d6SDan Williams 
433a1f3e4d6SDan Williams 	nd_region = to_nd_region(dev);
434a1f3e4d6SDan Williams 	for (i = 0; i < nd_region->ndr_mappings; i++) {
435a1f3e4d6SDan Williams 		nd_mapping  = &nd_region->mapping[i];
436a1f3e4d6SDan Williams 		if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
437a1f3e4d6SDan Williams 			break;
438a1f3e4d6SDan Williams 	}
439a1f3e4d6SDan Williams 
440a1f3e4d6SDan Williams 	if (i >= nd_region->ndr_mappings)
441a1f3e4d6SDan Williams 		return 0;
442a1f3e4d6SDan Williams 
443a1f3e4d6SDan Williams 	ndd = to_ndd(nd_mapping);
444a1f3e4d6SDan Williams 	map_end = nd_mapping->start + nd_mapping->size - 1;
445a1f3e4d6SDan Williams 	blk_start = nd_mapping->start;
446762d067dSDan Williams 
447762d067dSDan Williams 	/*
448762d067dSDan Williams 	 * In the allocation case ->res is set to free space that we are
449762d067dSDan Williams 	 * looking to validate against PMEM aliasing collision rules
450762d067dSDan Williams 	 * (i.e. BLK is allocated after all aliased PMEM).
451762d067dSDan Williams 	 */
452762d067dSDan Williams 	if (info->res) {
453762d067dSDan Williams 		if (info->res->start >= nd_mapping->start
454762d067dSDan Williams 				&& info->res->start < map_end)
455762d067dSDan Williams 			/* pass */;
456762d067dSDan Williams 		else
457762d067dSDan Williams 			return 0;
458762d067dSDan Williams 	}
459762d067dSDan Williams 
460a1f3e4d6SDan Williams  retry:
461a1f3e4d6SDan Williams 	/*
462a1f3e4d6SDan Williams 	 * Find the free dpa from the end of the last pmem allocation to
463fe514739SDan Williams 	 * the end of the interleave-set mapping.
464a1f3e4d6SDan Williams 	 */
465a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
466fe514739SDan Williams 		if (strncmp(res->name, "pmem", 4) != 0)
467fe514739SDan Williams 			continue;
468a1f3e4d6SDan Williams 		if ((res->start >= blk_start && res->start < map_end)
469a1f3e4d6SDan Williams 				|| (res->end >= blk_start
470a1f3e4d6SDan Williams 					&& res->end <= map_end)) {
471fe514739SDan Williams 			new = max(blk_start, min(map_end + 1, res->end + 1));
472a1f3e4d6SDan Williams 			if (new != blk_start) {
473a1f3e4d6SDan Williams 				blk_start = new;
474a1f3e4d6SDan Williams 				goto retry;
475a1f3e4d6SDan Williams 			}
476a1f3e4d6SDan Williams 		}
477a1f3e4d6SDan Williams 	}
478a1f3e4d6SDan Williams 
479762d067dSDan Williams 	/* update the free space range with the probed blk_start */
480762d067dSDan Williams 	if (info->res && blk_start > info->res->start) {
481762d067dSDan Williams 		info->res->start = max(info->res->start, blk_start);
482762d067dSDan Williams 		if (info->res->start > info->res->end)
483762d067dSDan Williams 			info->res->end = info->res->start - 1;
484762d067dSDan Williams 		return 1;
485762d067dSDan Williams 	}
486762d067dSDan Williams 
487fe514739SDan Williams 	info->available -= blk_start - nd_mapping->start;
488762d067dSDan Williams 
489a1f3e4d6SDan Williams 	return 0;
490a1f3e4d6SDan Williams }
491a1f3e4d6SDan Williams 
492bf9bccc1SDan Williams /**
4931b40e09aSDan Williams  * nd_blk_available_dpa - account the unused dpa of BLK region
4941b40e09aSDan Williams  * @nd_mapping: container of dpa-resource-root + labels
4951b40e09aSDan Williams  *
496a1f3e4d6SDan Williams  * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but
497a1f3e4d6SDan Williams  * we arrange for them to never start at an lower dpa than the last
498a1f3e4d6SDan Williams  * PMEM allocation in an aliased region.
4991b40e09aSDan Williams  */
500a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
5011b40e09aSDan Williams {
502a1f3e4d6SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
503a1f3e4d6SDan Williams 	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
5041b40e09aSDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
505a1f3e4d6SDan Williams 	struct blk_alloc_info info = {
506a1f3e4d6SDan Williams 		.nd_mapping = nd_mapping,
507a1f3e4d6SDan Williams 		.available = nd_mapping->size,
508762d067dSDan Williams 		.res = NULL,
509a1f3e4d6SDan Williams 	};
5101b40e09aSDan Williams 	struct resource *res;
5111b40e09aSDan Williams 
5121b40e09aSDan Williams 	if (!ndd)
5131b40e09aSDan Williams 		return 0;
5141b40e09aSDan Williams 
515a1f3e4d6SDan Williams 	device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy);
5161b40e09aSDan Williams 
517a1f3e4d6SDan Williams 	/* now account for busy blk allocations in unaliased dpa */
518a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
519a1f3e4d6SDan Williams 		if (strncmp(res->name, "blk", 3) != 0)
520a1f3e4d6SDan Williams 			continue;
521fe514739SDan Williams 		info.available -= resource_size(res);
5221b40e09aSDan Williams 	}
5231b40e09aSDan Williams 
524a1f3e4d6SDan Williams 	return info.available;
5251b40e09aSDan Williams }
5261b40e09aSDan Williams 
5271b40e09aSDan Williams /**
52812e3129eSKeith Busch  * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max
52912e3129eSKeith Busch  *			   contiguous unallocated dpa range.
53012e3129eSKeith Busch  * @nd_region: constrain available space check to this reference region
53112e3129eSKeith Busch  * @nd_mapping: container of dpa-resource-root + labels
53212e3129eSKeith Busch  */
53312e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
53412e3129eSKeith Busch 					   struct nd_mapping *nd_mapping)
53512e3129eSKeith Busch {
53612e3129eSKeith Busch 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
53712e3129eSKeith Busch 	struct nvdimm_bus *nvdimm_bus;
53812e3129eSKeith Busch 	resource_size_t max = 0;
53912e3129eSKeith Busch 	struct resource *res;
54012e3129eSKeith Busch 
54112e3129eSKeith Busch 	/* if a dimm is disabled the available capacity is zero */
54212e3129eSKeith Busch 	if (!ndd)
54312e3129eSKeith Busch 		return 0;
54412e3129eSKeith Busch 
54512e3129eSKeith Busch 	nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
54612e3129eSKeith Busch 	if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm))
54712e3129eSKeith Busch 		return 0;
54812e3129eSKeith Busch 	for_each_dpa_resource(ndd, res) {
54912e3129eSKeith Busch 		if (strcmp(res->name, "pmem-reserve") != 0)
55012e3129eSKeith Busch 			continue;
55112e3129eSKeith Busch 		if (resource_size(res) > max)
55212e3129eSKeith Busch 			max = resource_size(res);
55312e3129eSKeith Busch 	}
55412e3129eSKeith Busch 	release_free_pmem(nvdimm_bus, nd_mapping);
55512e3129eSKeith Busch 	return max;
55612e3129eSKeith Busch }
55712e3129eSKeith Busch 
55812e3129eSKeith Busch /**
559bf9bccc1SDan Williams  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
560bf9bccc1SDan Williams  * @nd_mapping: container of dpa-resource-root + labels
561bf9bccc1SDan Williams  * @nd_region: constrain available space check to this reference region
562bf9bccc1SDan Williams  * @overlap: calculate available space assuming this level of overlap
563bf9bccc1SDan Williams  *
564bf9bccc1SDan Williams  * Validate that a PMEM label, if present, aligns with the start of an
565bf9bccc1SDan Williams  * interleave set and truncate the available size at the lowest BLK
566bf9bccc1SDan Williams  * overlap point.
567bf9bccc1SDan Williams  *
568bf9bccc1SDan Williams  * The expectation is that this routine is called multiple times as it
569bf9bccc1SDan Williams  * probes for the largest BLK encroachment for any single member DIMM of
570bf9bccc1SDan Williams  * the interleave set.  Once that value is determined the PMEM-limit for
571bf9bccc1SDan Williams  * the set can be established.
572bf9bccc1SDan Williams  */
573bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
574bf9bccc1SDan Williams 		struct nd_mapping *nd_mapping, resource_size_t *overlap)
575bf9bccc1SDan Williams {
576bf9bccc1SDan Williams 	resource_size_t map_start, map_end, busy = 0, available, blk_start;
577bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
578bf9bccc1SDan Williams 	struct resource *res;
579bf9bccc1SDan Williams 	const char *reason;
580bf9bccc1SDan Williams 
581bf9bccc1SDan Williams 	if (!ndd)
582bf9bccc1SDan Williams 		return 0;
583bf9bccc1SDan Williams 
584bf9bccc1SDan Williams 	map_start = nd_mapping->start;
585bf9bccc1SDan Williams 	map_end = map_start + nd_mapping->size - 1;
586bf9bccc1SDan Williams 	blk_start = max(map_start, map_end + 1 - *overlap);
587a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
588bf9bccc1SDan Williams 		if (res->start >= map_start && res->start < map_end) {
589bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0)
590a1f3e4d6SDan Williams 				blk_start = min(blk_start,
591a1f3e4d6SDan Williams 						max(map_start, res->start));
592a1f3e4d6SDan Williams 			else if (res->end > map_end) {
593bf9bccc1SDan Williams 				reason = "misaligned to iset";
594bf9bccc1SDan Williams 				goto err;
595a1f3e4d6SDan Williams 			} else
596bf9bccc1SDan Williams 				busy += resource_size(res);
597bf9bccc1SDan Williams 		} else if (res->end >= map_start && res->end <= map_end) {
598bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0) {
599bf9bccc1SDan Williams 				/*
600bf9bccc1SDan Williams 				 * If a BLK allocation overlaps the start of
601bf9bccc1SDan Williams 				 * PMEM the entire interleave set may now only
602bf9bccc1SDan Williams 				 * be used for BLK.
603bf9bccc1SDan Williams 				 */
604bf9bccc1SDan Williams 				blk_start = map_start;
605a1f3e4d6SDan Williams 			} else
606a1f3e4d6SDan Williams 				busy += resource_size(res);
607bf9bccc1SDan Williams 		} else if (map_start > res->start && map_start < res->end) {
608bf9bccc1SDan Williams 			/* total eclipse of the mapping */
609bf9bccc1SDan Williams 			busy += nd_mapping->size;
610bf9bccc1SDan Williams 			blk_start = map_start;
611bf9bccc1SDan Williams 		}
612a1f3e4d6SDan Williams 	}
613bf9bccc1SDan Williams 
614bf9bccc1SDan Williams 	*overlap = map_end + 1 - blk_start;
615bf9bccc1SDan Williams 	available = blk_start - map_start;
616bf9bccc1SDan Williams 	if (busy < available)
617bf9bccc1SDan Williams 		return available - busy;
618bf9bccc1SDan Williams 	return 0;
619bf9bccc1SDan Williams 
620bf9bccc1SDan Williams  err:
621bf9bccc1SDan Williams 	nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
622bf9bccc1SDan Williams 	return 0;
623bf9bccc1SDan Williams }
624bf9bccc1SDan Williams 
6254a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
6264a826c83SDan Williams {
6274a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
6284a826c83SDan Williams 	kfree(res->name);
6294a826c83SDan Williams 	__release_region(&ndd->dpa, res->start, resource_size(res));
6304a826c83SDan Williams }
6314a826c83SDan Williams 
6324a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
6334a826c83SDan Williams 		struct nd_label_id *label_id, resource_size_t start,
6344a826c83SDan Williams 		resource_size_t n)
6354a826c83SDan Williams {
6364a826c83SDan Williams 	char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
6374a826c83SDan Williams 	struct resource *res;
6384a826c83SDan Williams 
6394a826c83SDan Williams 	if (!name)
6404a826c83SDan Williams 		return NULL;
6414a826c83SDan Williams 
6424a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
6434a826c83SDan Williams 	res = __request_region(&ndd->dpa, start, n, name, 0);
6444a826c83SDan Williams 	if (!res)
6454a826c83SDan Williams 		kfree(name);
6464a826c83SDan Williams 	return res;
6474a826c83SDan Williams }
6484a826c83SDan Williams 
649bf9bccc1SDan Williams /**
650bf9bccc1SDan Williams  * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
651bf9bccc1SDan Williams  * @nvdimm: container of dpa-resource-root + labels
652bf9bccc1SDan Williams  * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid>
653bf9bccc1SDan Williams  */
654bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
655bf9bccc1SDan Williams 		struct nd_label_id *label_id)
656bf9bccc1SDan Williams {
657bf9bccc1SDan Williams 	resource_size_t allocated = 0;
658bf9bccc1SDan Williams 	struct resource *res;
659bf9bccc1SDan Williams 
660bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
661bf9bccc1SDan Williams 		if (strcmp(res->name, label_id->id) == 0)
662bf9bccc1SDan Williams 			allocated += resource_size(res);
663bf9bccc1SDan Williams 
664bf9bccc1SDan Williams 	return allocated;
665bf9bccc1SDan Williams }
666bf9bccc1SDan Williams 
6674d88a97aSDan Williams static int count_dimms(struct device *dev, void *c)
6684d88a97aSDan Williams {
6694d88a97aSDan Williams 	int *count = c;
6704d88a97aSDan Williams 
6714d88a97aSDan Williams 	if (is_nvdimm(dev))
6724d88a97aSDan Williams 		(*count)++;
6734d88a97aSDan Williams 	return 0;
6744d88a97aSDan Williams }
6754d88a97aSDan Williams 
6764d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
6774d88a97aSDan Williams {
6784d88a97aSDan Williams 	int count = 0;
6794d88a97aSDan Williams 	/* Flush any possible dimm registration failures */
6804d88a97aSDan Williams 	nd_synchronize();
6814d88a97aSDan Williams 
6824d88a97aSDan Williams 	device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
683426824d6SDan Williams 	dev_dbg(&nvdimm_bus->dev, "count: %d\n", count);
6844d88a97aSDan Williams 	if (count != dimm_count)
6854d88a97aSDan Williams 		return -ENXIO;
6864d88a97aSDan Williams 	return 0;
6874d88a97aSDan Williams }
6884d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
689b354aba0SDan Williams 
690b354aba0SDan Williams void __exit nvdimm_devs_exit(void)
691b354aba0SDan Williams {
692b354aba0SDan Williams 	ida_destroy(&dimm_ida);
693b354aba0SDan Williams }
694