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