xref: /openbmc/linux/drivers/nvdimm/dimm_devs.c (revision aee65987)
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"
234d88a97aSDan Williams #include "nd.h"
24e6dfb2deSDan Williams 
25e6dfb2deSDan Williams static DEFINE_IDA(dimm_ida);
26e6dfb2deSDan Williams 
274d88a97aSDan Williams /*
284d88a97aSDan Williams  * Retrieve bus and dimm handle and return if this bus supports
294d88a97aSDan Williams  * get_config_data commands
304d88a97aSDan Williams  */
31aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev)
324d88a97aSDan Williams {
33aee65987SToshi Kani 	struct nvdimm *nvdimm = to_nvdimm(dev);
344d88a97aSDan Williams 
35aee65987SToshi Kani 	if (!nvdimm->cmd_mask ||
36aee65987SToshi Kani 	    !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
37aee65987SToshi Kani 		if (nvdimm->flags & NDD_ALIASING)
384d88a97aSDan Williams 			return -ENXIO;
39aee65987SToshi Kani 		else
40aee65987SToshi Kani 			return -ENOTTY;
41aee65987SToshi Kani 	}
424d88a97aSDan Williams 
434d88a97aSDan Williams 	return 0;
444d88a97aSDan Williams }
454d88a97aSDan Williams 
464d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd)
474d88a97aSDan Williams {
48aee65987SToshi Kani 	int rc;
494d88a97aSDan Williams 
50aee65987SToshi Kani 	if (!ndd)
51aee65987SToshi Kani 		return -EINVAL;
52aee65987SToshi Kani 
53aee65987SToshi Kani 	rc = nvdimm_check_config_data(ndd->dev);
54aee65987SToshi Kani 	if (rc)
554d88a97aSDan Williams 		dev_dbg(ndd->dev, "%pf: %s error: %d\n",
564d88a97aSDan Williams 				__builtin_return_address(0), __func__, rc);
574d88a97aSDan Williams 	return rc;
584d88a97aSDan Williams }
594d88a97aSDan Williams 
604d88a97aSDan Williams /**
614d88a97aSDan Williams  * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
624d88a97aSDan Williams  * @nvdimm: dimm to initialize
634d88a97aSDan Williams  */
644d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
654d88a97aSDan Williams {
664d88a97aSDan Williams 	struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
674d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
684d88a97aSDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
694d88a97aSDan Williams 	int rc = validate_dimm(ndd);
704d88a97aSDan Williams 
714d88a97aSDan Williams 	if (rc)
724d88a97aSDan Williams 		return rc;
734d88a97aSDan Williams 
744d88a97aSDan Williams 	if (cmd->config_size)
754d88a97aSDan Williams 		return 0; /* already valid */
764d88a97aSDan Williams 
774d88a97aSDan Williams 	memset(cmd, 0, sizeof(*cmd));
784d88a97aSDan Williams 	nd_desc = nvdimm_bus->nd_desc;
794d88a97aSDan Williams 	return nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
80aef25338SDan Williams 			ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), NULL);
814d88a97aSDan Williams }
824d88a97aSDan Williams 
834d88a97aSDan Williams int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
844d88a97aSDan Williams {
854d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
864d88a97aSDan Williams 	struct nd_cmd_get_config_data_hdr *cmd;
874d88a97aSDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
884d88a97aSDan Williams 	int rc = validate_dimm(ndd);
894d88a97aSDan Williams 	u32 max_cmd_size, config_size;
904d88a97aSDan Williams 	size_t offset;
914d88a97aSDan Williams 
924d88a97aSDan Williams 	if (rc)
934d88a97aSDan Williams 		return rc;
944d88a97aSDan Williams 
954d88a97aSDan Williams 	if (ndd->data)
964d88a97aSDan Williams 		return 0;
974d88a97aSDan Williams 
984a826c83SDan Williams 	if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0
994a826c83SDan Williams 			|| ndd->nsarea.config_size < ND_LABEL_MIN_SIZE) {
1004a826c83SDan Williams 		dev_dbg(ndd->dev, "failed to init config data area: (%d:%d)\n",
1014a826c83SDan Williams 				ndd->nsarea.max_xfer, ndd->nsarea.config_size);
1024d88a97aSDan Williams 		return -ENXIO;
1034a826c83SDan Williams 	}
1044d88a97aSDan Williams 
1054d88a97aSDan Williams 	ndd->data = kmalloc(ndd->nsarea.config_size, GFP_KERNEL);
1064d88a97aSDan Williams 	if (!ndd->data)
1074d88a97aSDan Williams 		ndd->data = vmalloc(ndd->nsarea.config_size);
1084d88a97aSDan Williams 
1094d88a97aSDan Williams 	if (!ndd->data)
1104d88a97aSDan Williams 		return -ENOMEM;
1114d88a97aSDan Williams 
1124d88a97aSDan Williams 	max_cmd_size = min_t(u32, PAGE_SIZE, ndd->nsarea.max_xfer);
1134d88a97aSDan Williams 	cmd = kzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
1144d88a97aSDan Williams 	if (!cmd)
1154d88a97aSDan Williams 		return -ENOMEM;
1164d88a97aSDan Williams 
1174d88a97aSDan Williams 	nd_desc = nvdimm_bus->nd_desc;
1184d88a97aSDan Williams 	for (config_size = ndd->nsarea.config_size, offset = 0;
1194d88a97aSDan Williams 			config_size; config_size -= cmd->in_length,
1204d88a97aSDan Williams 			offset += cmd->in_length) {
1214d88a97aSDan Williams 		cmd->in_length = min(config_size, max_cmd_size);
1224d88a97aSDan Williams 		cmd->in_offset = offset;
1234d88a97aSDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
1244d88a97aSDan Williams 				ND_CMD_GET_CONFIG_DATA, cmd,
125aef25338SDan Williams 				cmd->in_length + sizeof(*cmd), NULL);
1264d88a97aSDan Williams 		if (rc || cmd->status) {
1274d88a97aSDan Williams 			rc = -ENXIO;
1284d88a97aSDan Williams 			break;
1294d88a97aSDan Williams 		}
1304d88a97aSDan Williams 		memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length);
1314d88a97aSDan Williams 	}
1324d88a97aSDan Williams 	dev_dbg(ndd->dev, "%s: len: %zu rc: %d\n", __func__, offset, rc);
1334d88a97aSDan Williams 	kfree(cmd);
1344d88a97aSDan Williams 
1354d88a97aSDan Williams 	return rc;
1364d88a97aSDan Williams }
1374d88a97aSDan Williams 
138f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
139f524bf27SDan Williams 		void *buf, size_t len)
140f524bf27SDan Williams {
141f524bf27SDan Williams 	int rc = validate_dimm(ndd);
142f524bf27SDan Williams 	size_t max_cmd_size, buf_offset;
143f524bf27SDan Williams 	struct nd_cmd_set_config_hdr *cmd;
144f524bf27SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
145f524bf27SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
146f524bf27SDan Williams 
147f524bf27SDan Williams 	if (rc)
148f524bf27SDan Williams 		return rc;
149f524bf27SDan Williams 
150f524bf27SDan Williams 	if (!ndd->data)
151f524bf27SDan Williams 		return -ENXIO;
152f524bf27SDan Williams 
153f524bf27SDan Williams 	if (offset + len > ndd->nsarea.config_size)
154f524bf27SDan Williams 		return -ENXIO;
155f524bf27SDan Williams 
156f524bf27SDan Williams 	max_cmd_size = min_t(u32, PAGE_SIZE, len);
157f524bf27SDan Williams 	max_cmd_size = min_t(u32, max_cmd_size, ndd->nsarea.max_xfer);
158f524bf27SDan Williams 	cmd = kzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
159f524bf27SDan Williams 	if (!cmd)
160f524bf27SDan Williams 		return -ENOMEM;
161f524bf27SDan Williams 
162f524bf27SDan Williams 	for (buf_offset = 0; len; len -= cmd->in_length,
163f524bf27SDan Williams 			buf_offset += cmd->in_length) {
164f524bf27SDan Williams 		size_t cmd_size;
165f524bf27SDan Williams 		u32 *status;
166f524bf27SDan Williams 
167f524bf27SDan Williams 		cmd->in_offset = offset + buf_offset;
168f524bf27SDan Williams 		cmd->in_length = min(max_cmd_size, len);
169f524bf27SDan Williams 		memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
170f524bf27SDan Williams 
171f524bf27SDan Williams 		/* status is output in the last 4-bytes of the command buffer */
172f524bf27SDan Williams 		cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
173f524bf27SDan Williams 		status = ((void *) cmd) + cmd_size - sizeof(u32);
174f524bf27SDan Williams 
175f524bf27SDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
176aef25338SDan Williams 				ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL);
177f524bf27SDan Williams 		if (rc || *status) {
178f524bf27SDan Williams 			rc = rc ? rc : -ENXIO;
179f524bf27SDan Williams 			break;
180f524bf27SDan Williams 		}
181f524bf27SDan Williams 	}
182f524bf27SDan Williams 	kfree(cmd);
183f524bf27SDan Williams 
184f524bf27SDan Williams 	return rc;
185f524bf27SDan Williams }
186f524bf27SDan Williams 
187e6dfb2deSDan Williams static void nvdimm_release(struct device *dev)
188e6dfb2deSDan Williams {
189e6dfb2deSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
190e6dfb2deSDan Williams 
191e6dfb2deSDan Williams 	ida_simple_remove(&dimm_ida, nvdimm->id);
192e6dfb2deSDan Williams 	kfree(nvdimm);
193e6dfb2deSDan Williams }
194e6dfb2deSDan Williams 
195e6dfb2deSDan Williams static struct device_type nvdimm_device_type = {
196e6dfb2deSDan Williams 	.name = "nvdimm",
197e6dfb2deSDan Williams 	.release = nvdimm_release,
198e6dfb2deSDan Williams };
199e6dfb2deSDan Williams 
20062232e45SDan Williams bool is_nvdimm(struct device *dev)
201e6dfb2deSDan Williams {
202e6dfb2deSDan Williams 	return dev->type == &nvdimm_device_type;
203e6dfb2deSDan Williams }
204e6dfb2deSDan Williams 
205e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev)
206e6dfb2deSDan Williams {
207e6dfb2deSDan Williams 	struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
208e6dfb2deSDan Williams 
209e6dfb2deSDan Williams 	WARN_ON(!is_nvdimm(dev));
210e6dfb2deSDan Williams 	return nvdimm;
211e6dfb2deSDan Williams }
212e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm);
213e6dfb2deSDan Williams 
214047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr)
215047fc8a1SRoss Zwisler {
216047fc8a1SRoss Zwisler 	struct nd_region *nd_region = &ndbr->nd_region;
217047fc8a1SRoss Zwisler 	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
218047fc8a1SRoss Zwisler 
219047fc8a1SRoss Zwisler 	return nd_mapping->nvdimm;
220047fc8a1SRoss Zwisler }
221047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm);
222047fc8a1SRoss Zwisler 
223bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
224bf9bccc1SDan Williams {
225bf9bccc1SDan Williams 	struct nvdimm *nvdimm = nd_mapping->nvdimm;
226bf9bccc1SDan Williams 
227bf9bccc1SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
228bf9bccc1SDan Williams 
229bf9bccc1SDan Williams 	return dev_get_drvdata(&nvdimm->dev);
230bf9bccc1SDan Williams }
231bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd);
232bf9bccc1SDan Williams 
233bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref)
234bf9bccc1SDan Williams {
235bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
236bf9bccc1SDan Williams 	struct device *dev = ndd->dev;
237bf9bccc1SDan Williams 	struct resource *res, *_r;
238bf9bccc1SDan Williams 
239bf9bccc1SDan Williams 	dev_dbg(dev, "%s\n", __func__);
240bf9bccc1SDan Williams 
241bf9bccc1SDan Williams 	nvdimm_bus_lock(dev);
242bf9bccc1SDan Williams 	for_each_dpa_resource_safe(ndd, res, _r)
243bf9bccc1SDan Williams 		nvdimm_free_dpa(ndd, res);
244bf9bccc1SDan Williams 	nvdimm_bus_unlock(dev);
245bf9bccc1SDan Williams 
246a06a7576Syalin wang 	kvfree(ndd->data);
247bf9bccc1SDan Williams 	kfree(ndd);
248bf9bccc1SDan Williams 	put_device(dev);
249bf9bccc1SDan Williams }
250bf9bccc1SDan Williams 
251bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd)
252bf9bccc1SDan Williams {
253bf9bccc1SDan Williams 	kref_get(&ndd->kref);
254bf9bccc1SDan Williams }
255bf9bccc1SDan Williams 
256bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd)
257bf9bccc1SDan Williams {
258bf9bccc1SDan Williams 	if (ndd)
259bf9bccc1SDan Williams 		kref_put(&ndd->kref, nvdimm_drvdata_release);
260bf9bccc1SDan Williams }
261bf9bccc1SDan Williams 
262e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm)
263e6dfb2deSDan Williams {
264e6dfb2deSDan Williams 	return dev_name(&nvdimm->dev);
265e6dfb2deSDan Williams }
266e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name);
267e6dfb2deSDan Williams 
268ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
269ba9c8dd3SDan Williams {
270ba9c8dd3SDan Williams 	return &nvdimm->dev.kobj;
271ba9c8dd3SDan Williams }
272ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj);
273ba9c8dd3SDan Williams 
274e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
275e3654ecaSDan Williams {
276e3654ecaSDan Williams 	return nvdimm->cmd_mask;
277e3654ecaSDan Williams }
278e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
279e3654ecaSDan Williams 
280e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm)
281e6dfb2deSDan Williams {
28262232e45SDan Williams 	if (nvdimm)
283e6dfb2deSDan Williams 		return nvdimm->provider_data;
28462232e45SDan Williams 	return NULL;
285e6dfb2deSDan Williams }
286e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data);
287e6dfb2deSDan Williams 
28862232e45SDan Williams static ssize_t commands_show(struct device *dev,
28962232e45SDan Williams 		struct device_attribute *attr, char *buf)
29062232e45SDan Williams {
29162232e45SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
29262232e45SDan Williams 	int cmd, len = 0;
29362232e45SDan Williams 
294e3654ecaSDan Williams 	if (!nvdimm->cmd_mask)
29562232e45SDan Williams 		return sprintf(buf, "\n");
29662232e45SDan Williams 
297e3654ecaSDan Williams 	for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
29862232e45SDan Williams 		len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
29962232e45SDan Williams 	len += sprintf(buf + len, "\n");
30062232e45SDan Williams 	return len;
30162232e45SDan Williams }
30262232e45SDan Williams static DEVICE_ATTR_RO(commands);
30362232e45SDan Williams 
304eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr,
305eaf96153SDan Williams 		char *buf)
306eaf96153SDan Williams {
307eaf96153SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
308eaf96153SDan Williams 
309eaf96153SDan Williams 	/*
310eaf96153SDan Williams 	 * The state may be in the process of changing, userspace should
311eaf96153SDan Williams 	 * quiesce probing if it wants a static answer
312eaf96153SDan Williams 	 */
313eaf96153SDan Williams 	nvdimm_bus_lock(dev);
314eaf96153SDan Williams 	nvdimm_bus_unlock(dev);
315eaf96153SDan Williams 	return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy)
316eaf96153SDan Williams 			? "active" : "idle");
317eaf96153SDan Williams }
318eaf96153SDan Williams static DEVICE_ATTR_RO(state);
319eaf96153SDan Williams 
3200ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev,
3210ba1c634SDan Williams 		struct device_attribute *attr, char *buf)
3220ba1c634SDan Williams {
3230ba1c634SDan Williams 	struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
3240ba1c634SDan Williams 	ssize_t rc;
3250ba1c634SDan Williams 	u32 nfree;
3260ba1c634SDan Williams 
3270ba1c634SDan Williams 	if (!ndd)
3280ba1c634SDan Williams 		return -ENXIO;
3290ba1c634SDan Williams 
3300ba1c634SDan Williams 	nvdimm_bus_lock(dev);
3310ba1c634SDan Williams 	nfree = nd_label_nfree(ndd);
3320ba1c634SDan Williams 	if (nfree - 1 > nfree) {
3330ba1c634SDan Williams 		dev_WARN_ONCE(dev, 1, "we ate our last label?\n");
3340ba1c634SDan Williams 		nfree = 0;
3350ba1c634SDan Williams 	} else
3360ba1c634SDan Williams 		nfree--;
3370ba1c634SDan Williams 	rc = sprintf(buf, "%d\n", nfree);
3380ba1c634SDan Williams 	nvdimm_bus_unlock(dev);
3390ba1c634SDan Williams 	return rc;
3400ba1c634SDan Williams }
3410ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots);
3420ba1c634SDan Williams 
34362232e45SDan Williams static struct attribute *nvdimm_attributes[] = {
344eaf96153SDan Williams 	&dev_attr_state.attr,
34562232e45SDan Williams 	&dev_attr_commands.attr,
3460ba1c634SDan Williams 	&dev_attr_available_slots.attr,
34762232e45SDan Williams 	NULL,
34862232e45SDan Williams };
34962232e45SDan Williams 
35062232e45SDan Williams struct attribute_group nvdimm_attribute_group = {
35162232e45SDan Williams 	.attrs = nvdimm_attributes,
35262232e45SDan Williams };
35362232e45SDan Williams EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
35462232e45SDan Williams 
355e6dfb2deSDan Williams struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
35662232e45SDan Williams 		const struct attribute_group **groups, unsigned long flags,
357e5ae3b25SDan Williams 		unsigned long cmd_mask, int num_flush,
358e5ae3b25SDan Williams 		struct resource *flush_wpq)
359e6dfb2deSDan Williams {
360e6dfb2deSDan Williams 	struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
361e6dfb2deSDan Williams 	struct device *dev;
362e6dfb2deSDan Williams 
363e6dfb2deSDan Williams 	if (!nvdimm)
364e6dfb2deSDan Williams 		return NULL;
365e6dfb2deSDan Williams 
366e6dfb2deSDan Williams 	nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
367e6dfb2deSDan Williams 	if (nvdimm->id < 0) {
368e6dfb2deSDan Williams 		kfree(nvdimm);
369e6dfb2deSDan Williams 		return NULL;
370e6dfb2deSDan Williams 	}
371e6dfb2deSDan Williams 	nvdimm->provider_data = provider_data;
372e6dfb2deSDan Williams 	nvdimm->flags = flags;
373e3654ecaSDan Williams 	nvdimm->cmd_mask = cmd_mask;
374e5ae3b25SDan Williams 	nvdimm->num_flush = num_flush;
375e5ae3b25SDan Williams 	nvdimm->flush_wpq = flush_wpq;
376eaf96153SDan Williams 	atomic_set(&nvdimm->busy, 0);
377e6dfb2deSDan Williams 	dev = &nvdimm->dev;
378e6dfb2deSDan Williams 	dev_set_name(dev, "nmem%d", nvdimm->id);
379e6dfb2deSDan Williams 	dev->parent = &nvdimm_bus->dev;
380e6dfb2deSDan Williams 	dev->type = &nvdimm_device_type;
38162232e45SDan Williams 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
382e6dfb2deSDan Williams 	dev->groups = groups;
3834d88a97aSDan Williams 	nd_device_register(dev);
384e6dfb2deSDan Williams 
385e6dfb2deSDan Williams 	return nvdimm;
386e6dfb2deSDan Williams }
387e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_create);
3884d88a97aSDan Williams 
389bf9bccc1SDan Williams /**
3901b40e09aSDan Williams  * nd_blk_available_dpa - account the unused dpa of BLK region
3911b40e09aSDan Williams  * @nd_mapping: container of dpa-resource-root + labels
3921b40e09aSDan Williams  *
3931b40e09aSDan Williams  * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges.
3941b40e09aSDan Williams  */
3951b40e09aSDan Williams resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping)
3961b40e09aSDan Williams {
3971b40e09aSDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
3981b40e09aSDan Williams 	resource_size_t map_end, busy = 0, available;
3991b40e09aSDan Williams 	struct resource *res;
4001b40e09aSDan Williams 
4011b40e09aSDan Williams 	if (!ndd)
4021b40e09aSDan Williams 		return 0;
4031b40e09aSDan Williams 
4041b40e09aSDan Williams 	map_end = nd_mapping->start + nd_mapping->size - 1;
4051b40e09aSDan Williams 	for_each_dpa_resource(ndd, res)
4061b40e09aSDan Williams 		if (res->start >= nd_mapping->start && res->start < map_end) {
4071b40e09aSDan Williams 			resource_size_t end = min(map_end, res->end);
4081b40e09aSDan Williams 
4091b40e09aSDan Williams 			busy += end - res->start + 1;
4101b40e09aSDan Williams 		} else if (res->end >= nd_mapping->start
4111b40e09aSDan Williams 				&& res->end <= map_end) {
4121b40e09aSDan Williams 			busy += res->end - nd_mapping->start;
4131b40e09aSDan Williams 		} else if (nd_mapping->start > res->start
4141b40e09aSDan Williams 				&& nd_mapping->start < res->end) {
4151b40e09aSDan Williams 			/* total eclipse of the BLK region mapping */
4161b40e09aSDan Williams 			busy += nd_mapping->size;
4171b40e09aSDan Williams 		}
4181b40e09aSDan Williams 
4191b40e09aSDan Williams 	available = map_end - nd_mapping->start + 1;
4201b40e09aSDan Williams 	if (busy < available)
4211b40e09aSDan Williams 		return available - busy;
4221b40e09aSDan Williams 	return 0;
4231b40e09aSDan Williams }
4241b40e09aSDan Williams 
4251b40e09aSDan Williams /**
426bf9bccc1SDan Williams  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
427bf9bccc1SDan Williams  * @nd_mapping: container of dpa-resource-root + labels
428bf9bccc1SDan Williams  * @nd_region: constrain available space check to this reference region
429bf9bccc1SDan Williams  * @overlap: calculate available space assuming this level of overlap
430bf9bccc1SDan Williams  *
431bf9bccc1SDan Williams  * Validate that a PMEM label, if present, aligns with the start of an
432bf9bccc1SDan Williams  * interleave set and truncate the available size at the lowest BLK
433bf9bccc1SDan Williams  * overlap point.
434bf9bccc1SDan Williams  *
435bf9bccc1SDan Williams  * The expectation is that this routine is called multiple times as it
436bf9bccc1SDan Williams  * probes for the largest BLK encroachment for any single member DIMM of
437bf9bccc1SDan Williams  * the interleave set.  Once that value is determined the PMEM-limit for
438bf9bccc1SDan Williams  * the set can be established.
439bf9bccc1SDan Williams  */
440bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
441bf9bccc1SDan Williams 		struct nd_mapping *nd_mapping, resource_size_t *overlap)
442bf9bccc1SDan Williams {
443bf9bccc1SDan Williams 	resource_size_t map_start, map_end, busy = 0, available, blk_start;
444bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
445bf9bccc1SDan Williams 	struct resource *res;
446bf9bccc1SDan Williams 	const char *reason;
447bf9bccc1SDan Williams 
448bf9bccc1SDan Williams 	if (!ndd)
449bf9bccc1SDan Williams 		return 0;
450bf9bccc1SDan Williams 
451bf9bccc1SDan Williams 	map_start = nd_mapping->start;
452bf9bccc1SDan Williams 	map_end = map_start + nd_mapping->size - 1;
453bf9bccc1SDan Williams 	blk_start = max(map_start, map_end + 1 - *overlap);
454bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
455bf9bccc1SDan Williams 		if (res->start >= map_start && res->start < map_end) {
456bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0)
457bf9bccc1SDan Williams 				blk_start = min(blk_start, res->start);
458bf9bccc1SDan Williams 			else if (res->start != map_start) {
459bf9bccc1SDan Williams 				reason = "misaligned to iset";
460bf9bccc1SDan Williams 				goto err;
461bf9bccc1SDan Williams 			} else {
462bf9bccc1SDan Williams 				if (busy) {
463bf9bccc1SDan Williams 					reason = "duplicate overlapping PMEM reservations?";
464bf9bccc1SDan Williams 					goto err;
465bf9bccc1SDan Williams 				}
466bf9bccc1SDan Williams 				busy += resource_size(res);
467bf9bccc1SDan Williams 				continue;
468bf9bccc1SDan Williams 			}
469bf9bccc1SDan Williams 		} else if (res->end >= map_start && res->end <= map_end) {
470bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0) {
471bf9bccc1SDan Williams 				/*
472bf9bccc1SDan Williams 				 * If a BLK allocation overlaps the start of
473bf9bccc1SDan Williams 				 * PMEM the entire interleave set may now only
474bf9bccc1SDan Williams 				 * be used for BLK.
475bf9bccc1SDan Williams 				 */
476bf9bccc1SDan Williams 				blk_start = map_start;
477bf9bccc1SDan Williams 			} else {
478bf9bccc1SDan Williams 				reason = "misaligned to iset";
479bf9bccc1SDan Williams 				goto err;
480bf9bccc1SDan Williams 			}
481bf9bccc1SDan Williams 		} else if (map_start > res->start && map_start < res->end) {
482bf9bccc1SDan Williams 			/* total eclipse of the mapping */
483bf9bccc1SDan Williams 			busy += nd_mapping->size;
484bf9bccc1SDan Williams 			blk_start = map_start;
485bf9bccc1SDan Williams 		}
486bf9bccc1SDan Williams 
487bf9bccc1SDan Williams 	*overlap = map_end + 1 - blk_start;
488bf9bccc1SDan Williams 	available = blk_start - map_start;
489bf9bccc1SDan Williams 	if (busy < available)
490bf9bccc1SDan Williams 		return available - busy;
491bf9bccc1SDan Williams 	return 0;
492bf9bccc1SDan Williams 
493bf9bccc1SDan Williams  err:
494bf9bccc1SDan Williams 	/*
495bf9bccc1SDan Williams 	 * Something is wrong, PMEM must align with the start of the
496bf9bccc1SDan Williams 	 * interleave set, and there can only be one allocation per set.
497bf9bccc1SDan Williams 	 */
498bf9bccc1SDan Williams 	nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
499bf9bccc1SDan Williams 	return 0;
500bf9bccc1SDan Williams }
501bf9bccc1SDan Williams 
5024a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
5034a826c83SDan Williams {
5044a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
5054a826c83SDan Williams 	kfree(res->name);
5064a826c83SDan Williams 	__release_region(&ndd->dpa, res->start, resource_size(res));
5074a826c83SDan Williams }
5084a826c83SDan Williams 
5094a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
5104a826c83SDan Williams 		struct nd_label_id *label_id, resource_size_t start,
5114a826c83SDan Williams 		resource_size_t n)
5124a826c83SDan Williams {
5134a826c83SDan Williams 	char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
5144a826c83SDan Williams 	struct resource *res;
5154a826c83SDan Williams 
5164a826c83SDan Williams 	if (!name)
5174a826c83SDan Williams 		return NULL;
5184a826c83SDan Williams 
5194a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
5204a826c83SDan Williams 	res = __request_region(&ndd->dpa, start, n, name, 0);
5214a826c83SDan Williams 	if (!res)
5224a826c83SDan Williams 		kfree(name);
5234a826c83SDan Williams 	return res;
5244a826c83SDan Williams }
5254a826c83SDan Williams 
526bf9bccc1SDan Williams /**
527bf9bccc1SDan Williams  * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
528bf9bccc1SDan Williams  * @nvdimm: container of dpa-resource-root + labels
529bf9bccc1SDan Williams  * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid>
530bf9bccc1SDan Williams  */
531bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
532bf9bccc1SDan Williams 		struct nd_label_id *label_id)
533bf9bccc1SDan Williams {
534bf9bccc1SDan Williams 	resource_size_t allocated = 0;
535bf9bccc1SDan Williams 	struct resource *res;
536bf9bccc1SDan Williams 
537bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
538bf9bccc1SDan Williams 		if (strcmp(res->name, label_id->id) == 0)
539bf9bccc1SDan Williams 			allocated += resource_size(res);
540bf9bccc1SDan Williams 
541bf9bccc1SDan Williams 	return allocated;
542bf9bccc1SDan Williams }
543bf9bccc1SDan Williams 
5444d88a97aSDan Williams static int count_dimms(struct device *dev, void *c)
5454d88a97aSDan Williams {
5464d88a97aSDan Williams 	int *count = c;
5474d88a97aSDan Williams 
5484d88a97aSDan Williams 	if (is_nvdimm(dev))
5494d88a97aSDan Williams 		(*count)++;
5504d88a97aSDan Williams 	return 0;
5514d88a97aSDan Williams }
5524d88a97aSDan Williams 
5534d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
5544d88a97aSDan Williams {
5554d88a97aSDan Williams 	int count = 0;
5564d88a97aSDan Williams 	/* Flush any possible dimm registration failures */
5574d88a97aSDan Williams 	nd_synchronize();
5584d88a97aSDan Williams 
5594d88a97aSDan Williams 	device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
5604d88a97aSDan Williams 	dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count);
5614d88a97aSDan Williams 	if (count != dimm_count)
5624d88a97aSDan Williams 		return -ENXIO;
5634d88a97aSDan Williams 	return 0;
5644d88a97aSDan Williams }
5654d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
566b354aba0SDan Williams 
567b354aba0SDan Williams void __exit nvdimm_devs_exit(void)
568b354aba0SDan Williams {
569b354aba0SDan Williams 	ida_destroy(&dimm_ida);
570b354aba0SDan Williams }
571