15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e6dfb2deSDan Williams /* 3e6dfb2deSDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 4e6dfb2deSDan Williams */ 5e6dfb2deSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 6d5d30d5aSDan Williams #include <linux/moduleparam.h> 74d88a97aSDan Williams #include <linux/vmalloc.h> 8e6dfb2deSDan Williams #include <linux/device.h> 962232e45SDan Williams #include <linux/ndctl.h> 10e6dfb2deSDan Williams #include <linux/slab.h> 11e6dfb2deSDan Williams #include <linux/io.h> 12e6dfb2deSDan Williams #include <linux/fs.h> 13e6dfb2deSDan Williams #include <linux/mm.h> 14e6dfb2deSDan Williams #include "nd-core.h" 150ba1c634SDan Williams #include "label.h" 16ca6a4657SDan Williams #include "pmem.h" 174d88a97aSDan Williams #include "nd.h" 18e6dfb2deSDan Williams 19e6dfb2deSDan Williams static DEFINE_IDA(dimm_ida); 20e6dfb2deSDan Williams 214d88a97aSDan Williams /* 224d88a97aSDan Williams * Retrieve bus and dimm handle and return if this bus supports 234d88a97aSDan Williams * get_config_data commands 244d88a97aSDan Williams */ 25aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev) 264d88a97aSDan Williams { 27aee65987SToshi Kani struct nvdimm *nvdimm = to_nvdimm(dev); 284d88a97aSDan Williams 29aee65987SToshi Kani if (!nvdimm->cmd_mask || 30aee65987SToshi Kani !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { 31a0e37452SDan Williams if (test_bit(NDD_LABELING, &nvdimm->flags)) 324d88a97aSDan Williams return -ENXIO; 33aee65987SToshi Kani else 34aee65987SToshi Kani return -ENOTTY; 35aee65987SToshi Kani } 364d88a97aSDan Williams 374d88a97aSDan Williams return 0; 384d88a97aSDan Williams } 394d88a97aSDan Williams 404d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd) 414d88a97aSDan Williams { 42aee65987SToshi Kani int rc; 434d88a97aSDan Williams 44aee65987SToshi Kani if (!ndd) 45aee65987SToshi Kani return -EINVAL; 46aee65987SToshi Kani 47aee65987SToshi Kani rc = nvdimm_check_config_data(ndd->dev); 48aee65987SToshi Kani if (rc) 49d75f773cSSakari Ailus dev_dbg(ndd->dev, "%ps: %s error: %d\n", 504d88a97aSDan Williams __builtin_return_address(0), __func__, rc); 514d88a97aSDan Williams return rc; 524d88a97aSDan Williams } 534d88a97aSDan Williams 544d88a97aSDan Williams /** 554d88a97aSDan Williams * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area 564d88a97aSDan Williams * @nvdimm: dimm to initialize 574d88a97aSDan Williams */ 584d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd) 594d88a97aSDan Williams { 604d88a97aSDan Williams struct nd_cmd_get_config_size *cmd = &ndd->nsarea; 614d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 624d88a97aSDan Williams struct nvdimm_bus_descriptor *nd_desc; 634d88a97aSDan Williams int rc = validate_dimm(ndd); 649d62ed96SDan Williams int cmd_rc = 0; 654d88a97aSDan Williams 664d88a97aSDan Williams if (rc) 674d88a97aSDan Williams return rc; 684d88a97aSDan Williams 694d88a97aSDan Williams if (cmd->config_size) 704d88a97aSDan Williams return 0; /* already valid */ 714d88a97aSDan Williams 724d88a97aSDan Williams memset(cmd, 0, sizeof(*cmd)); 734d88a97aSDan Williams nd_desc = nvdimm_bus->nd_desc; 749d62ed96SDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 759d62ed96SDan Williams ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc); 769d62ed96SDan Williams if (rc < 0) 779d62ed96SDan Williams return rc; 789d62ed96SDan Williams return cmd_rc; 794d88a97aSDan Williams } 804d88a97aSDan Williams 812d657d17SAlexander Duyck int nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf, 822d657d17SAlexander Duyck size_t offset, size_t len) 834d88a97aSDan Williams { 844d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 852d657d17SAlexander Duyck struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 86e7c5a571SDan Williams int rc = validate_dimm(ndd), cmd_rc = 0; 874d88a97aSDan Williams struct nd_cmd_get_config_data_hdr *cmd; 882d657d17SAlexander Duyck size_t max_cmd_size, buf_offset; 894d88a97aSDan Williams 904d88a97aSDan Williams if (rc) 914d88a97aSDan Williams return rc; 924d88a97aSDan Williams 932d657d17SAlexander Duyck if (offset + len > ndd->nsarea.config_size) 944d88a97aSDan Williams return -ENXIO; 954d88a97aSDan Williams 962d657d17SAlexander Duyck max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 97d11cf4a7SDan Williams cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL); 984d88a97aSDan Williams if (!cmd) 994d88a97aSDan Williams return -ENOMEM; 1004d88a97aSDan Williams 1012d657d17SAlexander Duyck for (buf_offset = 0; len; 1022d657d17SAlexander Duyck len -= cmd->in_length, buf_offset += cmd->in_length) { 1032d657d17SAlexander Duyck size_t cmd_size; 1042d657d17SAlexander Duyck 1052d657d17SAlexander Duyck cmd->in_offset = offset + buf_offset; 1062d657d17SAlexander Duyck cmd->in_length = min(max_cmd_size, len); 1072d657d17SAlexander Duyck 1082d657d17SAlexander Duyck cmd_size = sizeof(*cmd) + cmd->in_length; 1092d657d17SAlexander Duyck 1104d88a97aSDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 1112d657d17SAlexander Duyck ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 112e7c5a571SDan Williams if (rc < 0) 113e7c5a571SDan Williams break; 114e7c5a571SDan Williams if (cmd_rc < 0) { 115e7c5a571SDan Williams rc = cmd_rc; 1164d88a97aSDan Williams break; 1174d88a97aSDan Williams } 1182d657d17SAlexander Duyck 1192d657d17SAlexander Duyck /* out_buf should be valid, copy it into our output buffer */ 1202d657d17SAlexander Duyck memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length); 1214d88a97aSDan Williams } 122d11cf4a7SDan Williams kvfree(cmd); 1234d88a97aSDan Williams 1244d88a97aSDan Williams return rc; 1254d88a97aSDan Williams } 1264d88a97aSDan Williams 127f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, 128f524bf27SDan Williams void *buf, size_t len) 129f524bf27SDan Williams { 130f524bf27SDan Williams size_t max_cmd_size, buf_offset; 131f524bf27SDan Williams struct nd_cmd_set_config_hdr *cmd; 132e7c5a571SDan Williams int rc = validate_dimm(ndd), cmd_rc = 0; 133f524bf27SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 134f524bf27SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 135f524bf27SDan Williams 136f524bf27SDan Williams if (rc) 137f524bf27SDan Williams return rc; 138f524bf27SDan Williams 139f524bf27SDan Williams if (offset + len > ndd->nsarea.config_size) 140f524bf27SDan Williams return -ENXIO; 141f524bf27SDan Williams 142d11cf4a7SDan Williams max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 143d11cf4a7SDan Williams cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL); 144f524bf27SDan Williams if (!cmd) 145f524bf27SDan Williams return -ENOMEM; 146f524bf27SDan Williams 147f524bf27SDan Williams for (buf_offset = 0; len; len -= cmd->in_length, 148f524bf27SDan Williams buf_offset += cmd->in_length) { 149f524bf27SDan Williams size_t cmd_size; 150f524bf27SDan Williams 151f524bf27SDan Williams cmd->in_offset = offset + buf_offset; 152f524bf27SDan Williams cmd->in_length = min(max_cmd_size, len); 153f524bf27SDan Williams memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length); 154f524bf27SDan Williams 155f524bf27SDan Williams /* status is output in the last 4-bytes of the command buffer */ 156f524bf27SDan Williams cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32); 157f524bf27SDan Williams 158f524bf27SDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 159e7c5a571SDan Williams ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 160e7c5a571SDan Williams if (rc < 0) 161e7c5a571SDan Williams break; 162e7c5a571SDan Williams if (cmd_rc < 0) { 163e7c5a571SDan Williams rc = cmd_rc; 164f524bf27SDan Williams break; 165f524bf27SDan Williams } 166f524bf27SDan Williams } 167d11cf4a7SDan Williams kvfree(cmd); 168f524bf27SDan Williams 169f524bf27SDan Williams return rc; 170f524bf27SDan Williams } 171f524bf27SDan Williams 172a0e37452SDan Williams void nvdimm_set_labeling(struct device *dev) 17342237e39SDan Williams { 17442237e39SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 17542237e39SDan Williams 176a0e37452SDan Williams set_bit(NDD_LABELING, &nvdimm->flags); 1778f078b38SDan Williams } 1788f078b38SDan Williams 1798f078b38SDan Williams void nvdimm_set_locked(struct device *dev) 1808f078b38SDan Williams { 1818f078b38SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 1828f078b38SDan Williams 1838f078b38SDan Williams set_bit(NDD_LOCKED, &nvdimm->flags); 18442237e39SDan Williams } 18542237e39SDan Williams 186d34cb808SDan Williams void nvdimm_clear_locked(struct device *dev) 187d34cb808SDan Williams { 188d34cb808SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 189d34cb808SDan Williams 190d34cb808SDan Williams clear_bit(NDD_LOCKED, &nvdimm->flags); 191d34cb808SDan Williams } 192d34cb808SDan Williams 193e6dfb2deSDan Williams static void nvdimm_release(struct device *dev) 194e6dfb2deSDan Williams { 195e6dfb2deSDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 196e6dfb2deSDan Williams 197e6dfb2deSDan Williams ida_simple_remove(&dimm_ida, nvdimm->id); 198e6dfb2deSDan Williams kfree(nvdimm); 199e6dfb2deSDan Williams } 200e6dfb2deSDan Williams 201e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev) 202e6dfb2deSDan Williams { 203e6dfb2deSDan Williams struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); 204e6dfb2deSDan Williams 205e6dfb2deSDan Williams WARN_ON(!is_nvdimm(dev)); 206e6dfb2deSDan Williams return nvdimm; 207e6dfb2deSDan Williams } 208e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm); 209e6dfb2deSDan Williams 210bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping) 211bf9bccc1SDan Williams { 212bf9bccc1SDan Williams struct nvdimm *nvdimm = nd_mapping->nvdimm; 213bf9bccc1SDan Williams 214bf9bccc1SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 215bf9bccc1SDan Williams 216bf9bccc1SDan Williams return dev_get_drvdata(&nvdimm->dev); 217bf9bccc1SDan Williams } 218bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd); 219bf9bccc1SDan Williams 220bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref) 221bf9bccc1SDan Williams { 222bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref); 223bf9bccc1SDan Williams struct device *dev = ndd->dev; 224bf9bccc1SDan Williams struct resource *res, *_r; 225bf9bccc1SDan Williams 226426824d6SDan Williams dev_dbg(dev, "trace\n"); 227bf9bccc1SDan Williams nvdimm_bus_lock(dev); 228bf9bccc1SDan Williams for_each_dpa_resource_safe(ndd, res, _r) 229bf9bccc1SDan Williams nvdimm_free_dpa(ndd, res); 230bf9bccc1SDan Williams nvdimm_bus_unlock(dev); 231bf9bccc1SDan Williams 232a06a7576Syalin wang kvfree(ndd->data); 233bf9bccc1SDan Williams kfree(ndd); 234bf9bccc1SDan Williams put_device(dev); 235bf9bccc1SDan Williams } 236bf9bccc1SDan Williams 237bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd) 238bf9bccc1SDan Williams { 239bf9bccc1SDan Williams kref_get(&ndd->kref); 240bf9bccc1SDan Williams } 241bf9bccc1SDan Williams 242bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd) 243bf9bccc1SDan Williams { 244bf9bccc1SDan Williams if (ndd) 245bf9bccc1SDan Williams kref_put(&ndd->kref, nvdimm_drvdata_release); 246bf9bccc1SDan Williams } 247bf9bccc1SDan Williams 248e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm) 249e6dfb2deSDan Williams { 250e6dfb2deSDan Williams return dev_name(&nvdimm->dev); 251e6dfb2deSDan Williams } 252e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name); 253e6dfb2deSDan Williams 254ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) 255ba9c8dd3SDan Williams { 256ba9c8dd3SDan Williams return &nvdimm->dev.kobj; 257ba9c8dd3SDan Williams } 258ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj); 259ba9c8dd3SDan Williams 260e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) 261e3654ecaSDan Williams { 262e3654ecaSDan Williams return nvdimm->cmd_mask; 263e3654ecaSDan Williams } 264e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask); 265e3654ecaSDan Williams 266e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm) 267e6dfb2deSDan Williams { 26862232e45SDan Williams if (nvdimm) 269e6dfb2deSDan Williams return nvdimm->provider_data; 27062232e45SDan Williams return NULL; 271e6dfb2deSDan Williams } 272e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data); 273e6dfb2deSDan Williams 27462232e45SDan Williams static ssize_t commands_show(struct device *dev, 27562232e45SDan Williams struct device_attribute *attr, char *buf) 27662232e45SDan Williams { 27762232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 27862232e45SDan Williams int cmd, len = 0; 27962232e45SDan Williams 280e3654ecaSDan Williams if (!nvdimm->cmd_mask) 28162232e45SDan Williams return sprintf(buf, "\n"); 28262232e45SDan Williams 283e3654ecaSDan Williams for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) 28462232e45SDan Williams len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); 28562232e45SDan Williams len += sprintf(buf + len, "\n"); 28662232e45SDan Williams return len; 28762232e45SDan Williams } 28862232e45SDan Williams static DEVICE_ATTR_RO(commands); 28962232e45SDan Williams 290efbf6f50SDan Williams static ssize_t flags_show(struct device *dev, 291efbf6f50SDan Williams struct device_attribute *attr, char *buf) 292efbf6f50SDan Williams { 293efbf6f50SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 294efbf6f50SDan Williams 2953b6c6c03SDan Williams return sprintf(buf, "%s%s\n", 296a0e37452SDan Williams test_bit(NDD_LABELING, &nvdimm->flags) ? "label " : "", 297efbf6f50SDan Williams test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : ""); 298efbf6f50SDan Williams } 299efbf6f50SDan Williams static DEVICE_ATTR_RO(flags); 300efbf6f50SDan Williams 301eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr, 302eaf96153SDan Williams char *buf) 303eaf96153SDan Williams { 304eaf96153SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 305eaf96153SDan Williams 306eaf96153SDan Williams /* 307eaf96153SDan Williams * The state may be in the process of changing, userspace should 308eaf96153SDan Williams * quiesce probing if it wants a static answer 309eaf96153SDan Williams */ 310eaf96153SDan Williams nvdimm_bus_lock(dev); 311eaf96153SDan Williams nvdimm_bus_unlock(dev); 312eaf96153SDan Williams return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) 313eaf96153SDan Williams ? "active" : "idle"); 314eaf96153SDan Williams } 315eaf96153SDan Williams static DEVICE_ATTR_RO(state); 316eaf96153SDan Williams 3177018c897SDan Williams static ssize_t __available_slots_show(struct nvdimm_drvdata *ndd, char *buf) 3180ba1c634SDan Williams { 3197018c897SDan Williams struct device *dev; 3200ba1c634SDan Williams ssize_t rc; 3210ba1c634SDan Williams u32 nfree; 3220ba1c634SDan Williams 3230ba1c634SDan Williams if (!ndd) 3240ba1c634SDan Williams return -ENXIO; 3250ba1c634SDan Williams 3267018c897SDan Williams dev = ndd->dev; 3270ba1c634SDan Williams nvdimm_bus_lock(dev); 3280ba1c634SDan Williams nfree = nd_label_nfree(ndd); 3290ba1c634SDan Williams if (nfree - 1 > nfree) { 3300ba1c634SDan Williams dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); 3310ba1c634SDan Williams nfree = 0; 3320ba1c634SDan Williams } else 3330ba1c634SDan Williams nfree--; 3340ba1c634SDan Williams rc = sprintf(buf, "%d\n", nfree); 3350ba1c634SDan Williams nvdimm_bus_unlock(dev); 3360ba1c634SDan Williams return rc; 3370ba1c634SDan Williams } 3387018c897SDan Williams 3397018c897SDan Williams static ssize_t available_slots_show(struct device *dev, 3407018c897SDan Williams struct device_attribute *attr, char *buf) 3417018c897SDan Williams { 3427018c897SDan Williams ssize_t rc; 3437018c897SDan Williams 344*81beea55SDan Williams device_lock(dev); 3457018c897SDan Williams rc = __available_slots_show(dev_get_drvdata(dev), buf); 346*81beea55SDan Williams device_unlock(dev); 3477018c897SDan Williams 3487018c897SDan Williams return rc; 3497018c897SDan Williams } 3500ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots); 3510ba1c634SDan Williams 3523c13e2acSDave Jiang __weak ssize_t security_show(struct device *dev, 353f2989396SDave Jiang struct device_attribute *attr, char *buf) 354f2989396SDave Jiang { 355f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 356f2989396SDave Jiang 3577c02d53dSJane Chu if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags)) 3587c02d53dSJane Chu return sprintf(buf, "overwrite\n"); 359d78c620aSDan Williams if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags)) 360f2989396SDave Jiang return sprintf(buf, "disabled\n"); 361d78c620aSDan Williams if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) 362f2989396SDave Jiang return sprintf(buf, "unlocked\n"); 363d78c620aSDan Williams if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags)) 364f2989396SDave Jiang return sprintf(buf, "locked\n"); 36589fa9d8eSDave Jiang return -ENOTTY; 366f2989396SDave Jiang } 367f2989396SDave Jiang 368d78c620aSDan Williams static ssize_t frozen_show(struct device *dev, 369d78c620aSDan Williams struct device_attribute *attr, char *buf) 370d78c620aSDan Williams { 371d78c620aSDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 372d78c620aSDan Williams 373d78c620aSDan Williams return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN, 374d78c620aSDan Williams &nvdimm->sec.flags)); 375f2989396SDave Jiang } 376d78c620aSDan Williams static DEVICE_ATTR_RO(frozen); 37737833fb7SDave Jiang 37837833fb7SDave Jiang static ssize_t security_store(struct device *dev, 37937833fb7SDave Jiang struct device_attribute *attr, const char *buf, size_t len) 38037833fb7SDave Jiang 38137833fb7SDave Jiang { 38237833fb7SDave Jiang ssize_t rc; 38337833fb7SDave Jiang 38437833fb7SDave Jiang /* 38537833fb7SDave Jiang * Require all userspace triggered security management to be 38637833fb7SDave Jiang * done while probing is idle and the DIMM is not in active use 38737833fb7SDave Jiang * in any region. 38837833fb7SDave Jiang */ 389*81beea55SDan Williams device_lock(dev); 39037833fb7SDave Jiang nvdimm_bus_lock(dev); 39137833fb7SDave Jiang wait_nvdimm_bus_probe_idle(dev); 3927b60422cSDan Williams rc = nvdimm_security_store(dev, buf, len); 39337833fb7SDave Jiang nvdimm_bus_unlock(dev); 394*81beea55SDan Williams device_unlock(dev); 39537833fb7SDave Jiang 39637833fb7SDave Jiang return rc; 39737833fb7SDave Jiang } 39837833fb7SDave Jiang static DEVICE_ATTR_RW(security); 399f2989396SDave Jiang 40062232e45SDan Williams static struct attribute *nvdimm_attributes[] = { 401eaf96153SDan Williams &dev_attr_state.attr, 402efbf6f50SDan Williams &dev_attr_flags.attr, 40362232e45SDan Williams &dev_attr_commands.attr, 4040ba1c634SDan Williams &dev_attr_available_slots.attr, 405f2989396SDave Jiang &dev_attr_security.attr, 406d78c620aSDan Williams &dev_attr_frozen.attr, 40762232e45SDan Williams NULL, 40862232e45SDan Williams }; 40962232e45SDan Williams 410f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) 411f2989396SDave Jiang { 412f2989396SDave Jiang struct device *dev = container_of(kobj, typeof(*dev), kobj); 413f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 414f2989396SDave Jiang 415d78c620aSDan Williams if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr) 416f2989396SDave Jiang return a->mode; 417d78c620aSDan Williams if (!nvdimm->sec.flags) 418f2989396SDave Jiang return 0; 419d78c620aSDan Williams 420d78c620aSDan Williams if (a == &dev_attr_security.attr) { 421d78c620aSDan Williams /* Are there any state mutation ops (make writable)? */ 422d2a4ac73SDave Jiang if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable 42364e77c8cSDave Jiang || nvdimm->sec.ops->change_key 4247d988097SDave Jiang || nvdimm->sec.ops->erase 4257d988097SDave Jiang || nvdimm->sec.ops->overwrite) 426f2989396SDave Jiang return a->mode; 42737833fb7SDave Jiang return 0444; 428f2989396SDave Jiang } 429f2989396SDave Jiang 430d78c620aSDan Williams if (nvdimm->sec.ops->freeze) 431d78c620aSDan Williams return a->mode; 432d78c620aSDan Williams return 0; 433d78c620aSDan Williams } 434d78c620aSDan Williams 435360eba7eSDan Williams static const struct attribute_group nvdimm_attribute_group = { 43662232e45SDan Williams .attrs = nvdimm_attributes, 437f2989396SDave Jiang .is_visible = nvdimm_visible, 43862232e45SDan Williams }; 439360eba7eSDan Williams 44048001ea5SDan Williams static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf) 44148001ea5SDan Williams { 44248001ea5SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 44348001ea5SDan Williams enum nvdimm_fwa_result result; 44448001ea5SDan Williams 44548001ea5SDan Williams if (!nvdimm->fw_ops) 44648001ea5SDan Williams return -EOPNOTSUPP; 44748001ea5SDan Williams 44848001ea5SDan Williams nvdimm_bus_lock(dev); 44948001ea5SDan Williams result = nvdimm->fw_ops->activate_result(nvdimm); 45048001ea5SDan Williams nvdimm_bus_unlock(dev); 45148001ea5SDan Williams 45248001ea5SDan Williams switch (result) { 45348001ea5SDan Williams case NVDIMM_FWA_RESULT_NONE: 45448001ea5SDan Williams return sprintf(buf, "none\n"); 45548001ea5SDan Williams case NVDIMM_FWA_RESULT_SUCCESS: 45648001ea5SDan Williams return sprintf(buf, "success\n"); 45748001ea5SDan Williams case NVDIMM_FWA_RESULT_FAIL: 45848001ea5SDan Williams return sprintf(buf, "fail\n"); 45948001ea5SDan Williams case NVDIMM_FWA_RESULT_NOTSTAGED: 46048001ea5SDan Williams return sprintf(buf, "not_staged\n"); 46148001ea5SDan Williams case NVDIMM_FWA_RESULT_NEEDRESET: 46248001ea5SDan Williams return sprintf(buf, "need_reset\n"); 46348001ea5SDan Williams default: 46448001ea5SDan Williams return -ENXIO; 46548001ea5SDan Williams } 46648001ea5SDan Williams } 46748001ea5SDan Williams static DEVICE_ATTR_ADMIN_RO(result); 46848001ea5SDan Williams 46948001ea5SDan Williams static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf) 47048001ea5SDan Williams { 47148001ea5SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 47248001ea5SDan Williams enum nvdimm_fwa_state state; 47348001ea5SDan Williams 47448001ea5SDan Williams if (!nvdimm->fw_ops) 47548001ea5SDan Williams return -EOPNOTSUPP; 47648001ea5SDan Williams 47748001ea5SDan Williams nvdimm_bus_lock(dev); 47848001ea5SDan Williams state = nvdimm->fw_ops->activate_state(nvdimm); 47948001ea5SDan Williams nvdimm_bus_unlock(dev); 48048001ea5SDan Williams 48148001ea5SDan Williams switch (state) { 48248001ea5SDan Williams case NVDIMM_FWA_IDLE: 48348001ea5SDan Williams return sprintf(buf, "idle\n"); 48448001ea5SDan Williams case NVDIMM_FWA_BUSY: 48548001ea5SDan Williams return sprintf(buf, "busy\n"); 48648001ea5SDan Williams case NVDIMM_FWA_ARMED: 48748001ea5SDan Williams return sprintf(buf, "armed\n"); 48848001ea5SDan Williams default: 48948001ea5SDan Williams return -ENXIO; 49048001ea5SDan Williams } 49148001ea5SDan Williams } 49248001ea5SDan Williams 49348001ea5SDan Williams static ssize_t activate_store(struct device *dev, struct device_attribute *attr, 49448001ea5SDan Williams const char *buf, size_t len) 49548001ea5SDan Williams { 49648001ea5SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 49748001ea5SDan Williams enum nvdimm_fwa_trigger arg; 49848001ea5SDan Williams int rc; 49948001ea5SDan Williams 50048001ea5SDan Williams if (!nvdimm->fw_ops) 50148001ea5SDan Williams return -EOPNOTSUPP; 50248001ea5SDan Williams 50348001ea5SDan Williams if (sysfs_streq(buf, "arm")) 50448001ea5SDan Williams arg = NVDIMM_FWA_ARM; 50548001ea5SDan Williams else if (sysfs_streq(buf, "disarm")) 50648001ea5SDan Williams arg = NVDIMM_FWA_DISARM; 50748001ea5SDan Williams else 50848001ea5SDan Williams return -EINVAL; 50948001ea5SDan Williams 51048001ea5SDan Williams nvdimm_bus_lock(dev); 51148001ea5SDan Williams rc = nvdimm->fw_ops->arm(nvdimm, arg); 51248001ea5SDan Williams nvdimm_bus_unlock(dev); 51348001ea5SDan Williams 51448001ea5SDan Williams if (rc < 0) 51548001ea5SDan Williams return rc; 51648001ea5SDan Williams return len; 51748001ea5SDan Williams } 51848001ea5SDan Williams static DEVICE_ATTR_ADMIN_RW(activate); 51948001ea5SDan Williams 52048001ea5SDan Williams static struct attribute *nvdimm_firmware_attributes[] = { 52148001ea5SDan Williams &dev_attr_activate.attr, 52248001ea5SDan Williams &dev_attr_result.attr, 52362c78927SZqiang NULL, 52448001ea5SDan Williams }; 52548001ea5SDan Williams 52648001ea5SDan Williams static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n) 52748001ea5SDan Williams { 52848001ea5SDan Williams struct device *dev = container_of(kobj, typeof(*dev), kobj); 52948001ea5SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 53048001ea5SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 53148001ea5SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 53248001ea5SDan Williams enum nvdimm_fwa_capability cap; 53348001ea5SDan Williams 53448001ea5SDan Williams if (!nd_desc->fw_ops) 53548001ea5SDan Williams return 0; 53648001ea5SDan Williams if (!nvdimm->fw_ops) 53748001ea5SDan Williams return 0; 53848001ea5SDan Williams 53948001ea5SDan Williams nvdimm_bus_lock(dev); 54048001ea5SDan Williams cap = nd_desc->fw_ops->capability(nd_desc); 54148001ea5SDan Williams nvdimm_bus_unlock(dev); 54248001ea5SDan Williams 54348001ea5SDan Williams if (cap < NVDIMM_FWA_CAP_QUIESCE) 54448001ea5SDan Williams return 0; 54548001ea5SDan Williams 54648001ea5SDan Williams return a->mode; 54748001ea5SDan Williams } 54848001ea5SDan Williams 54948001ea5SDan Williams static const struct attribute_group nvdimm_firmware_attribute_group = { 55048001ea5SDan Williams .name = "firmware", 55148001ea5SDan Williams .attrs = nvdimm_firmware_attributes, 55248001ea5SDan Williams .is_visible = nvdimm_firmware_visible, 55348001ea5SDan Williams }; 55448001ea5SDan Williams 555360eba7eSDan Williams static const struct attribute_group *nvdimm_attribute_groups[] = { 556360eba7eSDan Williams &nd_device_attribute_group, 557360eba7eSDan Williams &nvdimm_attribute_group, 55848001ea5SDan Williams &nvdimm_firmware_attribute_group, 559360eba7eSDan Williams NULL, 560360eba7eSDan Williams }; 561360eba7eSDan Williams 562360eba7eSDan Williams static const struct device_type nvdimm_device_type = { 563360eba7eSDan Williams .name = "nvdimm", 564360eba7eSDan Williams .release = nvdimm_release, 565360eba7eSDan Williams .groups = nvdimm_attribute_groups, 566360eba7eSDan Williams }; 567360eba7eSDan Williams 568360eba7eSDan Williams bool is_nvdimm(struct device *dev) 569360eba7eSDan Williams { 570360eba7eSDan Williams return dev->type == &nvdimm_device_type; 571360eba7eSDan Williams } 57262232e45SDan Williams 5734a0079bcSDan Williams static struct lock_class_key nvdimm_key; 5744a0079bcSDan Williams 575d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, 576d6548ae4SDave Jiang void *provider_data, const struct attribute_group **groups, 577d6548ae4SDave Jiang unsigned long flags, unsigned long cmd_mask, int num_flush, 578f2989396SDave Jiang struct resource *flush_wpq, const char *dimm_id, 579a1facc1fSDan Williams const struct nvdimm_security_ops *sec_ops, 580a1facc1fSDan Williams const struct nvdimm_fw_ops *fw_ops) 581e6dfb2deSDan Williams { 582e6dfb2deSDan Williams struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 583e6dfb2deSDan Williams struct device *dev; 584e6dfb2deSDan Williams 585e6dfb2deSDan Williams if (!nvdimm) 586e6dfb2deSDan Williams return NULL; 587e6dfb2deSDan Williams 588e6dfb2deSDan Williams nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); 589e6dfb2deSDan Williams if (nvdimm->id < 0) { 590e6dfb2deSDan Williams kfree(nvdimm); 591e6dfb2deSDan Williams return NULL; 592e6dfb2deSDan Williams } 593d6548ae4SDave Jiang 594d6548ae4SDave Jiang nvdimm->dimm_id = dimm_id; 595e6dfb2deSDan Williams nvdimm->provider_data = provider_data; 596e6dfb2deSDan Williams nvdimm->flags = flags; 597e3654ecaSDan Williams nvdimm->cmd_mask = cmd_mask; 598e5ae3b25SDan Williams nvdimm->num_flush = num_flush; 599e5ae3b25SDan Williams nvdimm->flush_wpq = flush_wpq; 600eaf96153SDan Williams atomic_set(&nvdimm->busy, 0); 601e6dfb2deSDan Williams dev = &nvdimm->dev; 602e6dfb2deSDan Williams dev_set_name(dev, "nmem%d", nvdimm->id); 603e6dfb2deSDan Williams dev->parent = &nvdimm_bus->dev; 604e6dfb2deSDan Williams dev->type = &nvdimm_device_type; 60562232e45SDan Williams dev->devt = MKDEV(nvdimm_major, nvdimm->id); 606e6dfb2deSDan Williams dev->groups = groups; 607f2989396SDave Jiang nvdimm->sec.ops = sec_ops; 608a1facc1fSDan Williams nvdimm->fw_ops = fw_ops; 6097d988097SDave Jiang nvdimm->sec.overwrite_tmo = 0; 6107d988097SDave Jiang INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query); 611f2989396SDave Jiang /* 612f2989396SDave Jiang * Security state must be initialized before device_add() for 613f2989396SDave Jiang * attribute visibility. 614f2989396SDave Jiang */ 61589fa9d8eSDave Jiang /* get security state and extended (master) state */ 616d78c620aSDan Williams nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 617d78c620aSDan Williams nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); 6184a0079bcSDan Williams device_initialize(dev); 6194a0079bcSDan Williams lockdep_set_class(&dev->mutex, &nvdimm_key); 6204d88a97aSDan Williams nd_device_register(dev); 621e6dfb2deSDan Williams 622e6dfb2deSDan Williams return nvdimm; 623e6dfb2deSDan Williams } 624d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create); 6254d88a97aSDan Williams 626fd14602dSDan Williams void nvdimm_delete(struct nvdimm *nvdimm) 627fd14602dSDan Williams { 628fd14602dSDan Williams struct device *dev = &nvdimm->dev; 629fd14602dSDan Williams bool dev_put = false; 630fd14602dSDan Williams 631fd14602dSDan Williams /* We are shutting down. Make state frozen artificially. */ 632fd14602dSDan Williams nvdimm_bus_lock(dev); 633fd14602dSDan Williams set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags); 634fd14602dSDan Williams if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags)) 635fd14602dSDan Williams dev_put = true; 636fd14602dSDan Williams nvdimm_bus_unlock(dev); 637fd14602dSDan Williams cancel_delayed_work_sync(&nvdimm->dwork); 638fd14602dSDan Williams if (dev_put) 639fd14602dSDan Williams put_device(dev); 640fd14602dSDan Williams nd_device_unregister(dev, ND_SYNC); 641fd14602dSDan Williams } 642fd14602dSDan Williams EXPORT_SYMBOL_GPL(nvdimm_delete); 643fd14602dSDan Williams 6441cd73865SDan Williams static void shutdown_security_notify(void *data) 6457d988097SDave Jiang { 6461cd73865SDan Williams struct nvdimm *nvdimm = data; 6471cd73865SDan Williams 6481cd73865SDan Williams sysfs_put(nvdimm->sec.overwrite_state); 6491cd73865SDan Williams } 6501cd73865SDan Williams 6511cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev) 6521cd73865SDan Williams { 6531cd73865SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 6541cd73865SDan Williams 655d78c620aSDan Williams if (!nvdimm->sec.flags || !nvdimm->sec.ops 6561cd73865SDan Williams || !nvdimm->sec.ops->overwrite) 6577d988097SDave Jiang return 0; 6581cd73865SDan Williams nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); 6591cd73865SDan Williams if (!nvdimm->sec.overwrite_state) 6601cd73865SDan Williams return -ENOMEM; 6611cd73865SDan Williams 6621cd73865SDan Williams return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm); 6637d988097SDave Jiang } 6647d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events); 6657d988097SDave Jiang 6667d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm) 6677d988097SDave Jiang { 6687d988097SDave Jiang return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 6697d988097SDave Jiang } 6707d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite); 6717d988097SDave Jiang 67237833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm) 67337833fb7SDave Jiang { 67437833fb7SDave Jiang int rc; 67537833fb7SDave Jiang 67637833fb7SDave Jiang WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 67737833fb7SDave Jiang 67837833fb7SDave Jiang if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) 67937833fb7SDave Jiang return -EOPNOTSUPP; 68037833fb7SDave Jiang 681d78c620aSDan Williams if (!nvdimm->sec.flags) 68237833fb7SDave Jiang return -EIO; 68337833fb7SDave Jiang 6847d988097SDave Jiang if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 6857d988097SDave Jiang dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n"); 6867d988097SDave Jiang return -EBUSY; 6877d988097SDave Jiang } 6887d988097SDave Jiang 68937833fb7SDave Jiang rc = nvdimm->sec.ops->freeze(nvdimm); 690d78c620aSDan Williams nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 69137833fb7SDave Jiang 69237833fb7SDave Jiang return rc; 69337833fb7SDave Jiang } 69437833fb7SDave Jiang 6952522afb8SDan Williams static unsigned long dpa_align(struct nd_region *nd_region) 6962522afb8SDan Williams { 6972522afb8SDan Williams struct device *dev = &nd_region->dev; 6982522afb8SDan Williams 6992522afb8SDan Williams if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev), 7002522afb8SDan Williams "bus lock required for capacity provision\n")) 7012522afb8SDan Williams return 0; 7022522afb8SDan Williams if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align 7032522afb8SDan Williams % nd_region->ndr_mappings, 7042522afb8SDan Williams "invalid region align %#lx mappings: %d\n", 7052522afb8SDan Williams nd_region->align, nd_region->ndr_mappings)) 7062522afb8SDan Williams return 0; 7072522afb8SDan Williams return nd_region->align / nd_region->ndr_mappings; 7082522afb8SDan Williams } 7092522afb8SDan Williams 7101b40e09aSDan Williams /** 71112e3129eSKeith Busch * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max 71212e3129eSKeith Busch * contiguous unallocated dpa range. 71312e3129eSKeith Busch * @nd_region: constrain available space check to this reference region 71412e3129eSKeith Busch * @nd_mapping: container of dpa-resource-root + labels 71512e3129eSKeith Busch */ 71612e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region, 71712e3129eSKeith Busch struct nd_mapping *nd_mapping) 71812e3129eSKeith Busch { 71912e3129eSKeith Busch struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 72012e3129eSKeith Busch struct nvdimm_bus *nvdimm_bus; 72112e3129eSKeith Busch resource_size_t max = 0; 72212e3129eSKeith Busch struct resource *res; 7232522afb8SDan Williams unsigned long align; 72412e3129eSKeith Busch 72512e3129eSKeith Busch /* if a dimm is disabled the available capacity is zero */ 72612e3129eSKeith Busch if (!ndd) 72712e3129eSKeith Busch return 0; 72812e3129eSKeith Busch 7292522afb8SDan Williams align = dpa_align(nd_region); 7302522afb8SDan Williams if (!align) 7312522afb8SDan Williams return 0; 7322522afb8SDan Williams 73312e3129eSKeith Busch nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 73412e3129eSKeith Busch if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm)) 73512e3129eSKeith Busch return 0; 73612e3129eSKeith Busch for_each_dpa_resource(ndd, res) { 7372522afb8SDan Williams resource_size_t start, end; 7382522afb8SDan Williams 73912e3129eSKeith Busch if (strcmp(res->name, "pmem-reserve") != 0) 74012e3129eSKeith Busch continue; 7412522afb8SDan Williams /* trim free space relative to current alignment setting */ 7422522afb8SDan Williams start = ALIGN(res->start, align); 7432522afb8SDan Williams end = ALIGN_DOWN(res->end + 1, align) - 1; 7442522afb8SDan Williams if (end < start) 7452522afb8SDan Williams continue; 7462522afb8SDan Williams if (end - start + 1 > max) 7472522afb8SDan Williams max = end - start + 1; 74812e3129eSKeith Busch } 74912e3129eSKeith Busch release_free_pmem(nvdimm_bus, nd_mapping); 75012e3129eSKeith Busch return max; 75112e3129eSKeith Busch } 75212e3129eSKeith Busch 75312e3129eSKeith Busch /** 754bf9bccc1SDan Williams * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 755bf9bccc1SDan Williams * @nd_mapping: container of dpa-resource-root + labels 756bf9bccc1SDan Williams * @nd_region: constrain available space check to this reference region 757bf9bccc1SDan Williams * 758bf9bccc1SDan Williams * Validate that a PMEM label, if present, aligns with the start of an 7593b6c6c03SDan Williams * interleave set. 760bf9bccc1SDan Williams */ 761bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 7623b6c6c03SDan Williams struct nd_mapping *nd_mapping) 763bf9bccc1SDan Williams { 764bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 7653b6c6c03SDan Williams resource_size_t map_start, map_end, busy = 0; 766bf9bccc1SDan Williams struct resource *res; 7672522afb8SDan Williams unsigned long align; 768bf9bccc1SDan Williams 769bf9bccc1SDan Williams if (!ndd) 770bf9bccc1SDan Williams return 0; 771bf9bccc1SDan Williams 7722522afb8SDan Williams align = dpa_align(nd_region); 7732522afb8SDan Williams if (!align) 7742522afb8SDan Williams return 0; 7752522afb8SDan Williams 776bf9bccc1SDan Williams map_start = nd_mapping->start; 777bf9bccc1SDan Williams map_end = map_start + nd_mapping->size - 1; 778a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 7792522afb8SDan Williams resource_size_t start, end; 7802522afb8SDan Williams 7812522afb8SDan Williams start = ALIGN_DOWN(res->start, align); 7822522afb8SDan Williams end = ALIGN(res->end + 1, align) - 1; 7832522afb8SDan Williams if (start >= map_start && start < map_end) { 7843b6c6c03SDan Williams if (end > map_end) { 7853b6c6c03SDan Williams nd_dbg_dpa(nd_region, ndd, res, 7863b6c6c03SDan Williams "misaligned to iset\n"); 7873b6c6c03SDan Williams return 0; 7883b6c6c03SDan Williams } 7892522afb8SDan Williams busy += end - start + 1; 7902522afb8SDan Williams } else if (end >= map_start && end <= map_end) { 7912522afb8SDan Williams busy += end - start + 1; 7922522afb8SDan Williams } else if (map_start > start && map_start < end) { 793bf9bccc1SDan Williams /* total eclipse of the mapping */ 794bf9bccc1SDan Williams busy += nd_mapping->size; 795bf9bccc1SDan Williams } 796a1f3e4d6SDan Williams } 797bf9bccc1SDan Williams 7983b6c6c03SDan Williams if (busy < nd_mapping->size) 7993b6c6c03SDan Williams return ALIGN_DOWN(nd_mapping->size - busy, align); 800bf9bccc1SDan Williams return 0; 801bf9bccc1SDan Williams } 802bf9bccc1SDan Williams 8034a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 8044a826c83SDan Williams { 8054a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8064a826c83SDan Williams kfree(res->name); 8074a826c83SDan Williams __release_region(&ndd->dpa, res->start, resource_size(res)); 8084a826c83SDan Williams } 8094a826c83SDan Williams 8104a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 8114a826c83SDan Williams struct nd_label_id *label_id, resource_size_t start, 8124a826c83SDan Williams resource_size_t n) 8134a826c83SDan Williams { 8144a826c83SDan Williams char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 8154a826c83SDan Williams struct resource *res; 8164a826c83SDan Williams 8174a826c83SDan Williams if (!name) 8184a826c83SDan Williams return NULL; 8194a826c83SDan Williams 8204a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8214a826c83SDan Williams res = __request_region(&ndd->dpa, start, n, name, 0); 8224a826c83SDan Williams if (!res) 8234a826c83SDan Williams kfree(name); 8244a826c83SDan Williams return res; 8254a826c83SDan Williams } 8264a826c83SDan Williams 827bf9bccc1SDan Williams /** 828bf9bccc1SDan Williams * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 829bf9bccc1SDan Williams * @nvdimm: container of dpa-resource-root + labels 8303b6c6c03SDan Williams * @label_id: dpa resource name of the form pmem-<human readable uuid> 831bf9bccc1SDan Williams */ 832bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 833bf9bccc1SDan Williams struct nd_label_id *label_id) 834bf9bccc1SDan Williams { 835bf9bccc1SDan Williams resource_size_t allocated = 0; 836bf9bccc1SDan Williams struct resource *res; 837bf9bccc1SDan Williams 838bf9bccc1SDan Williams for_each_dpa_resource(ndd, res) 839bf9bccc1SDan Williams if (strcmp(res->name, label_id->id) == 0) 840bf9bccc1SDan Williams allocated += resource_size(res); 841bf9bccc1SDan Williams 842bf9bccc1SDan Williams return allocated; 843bf9bccc1SDan Williams } 844bf9bccc1SDan Williams 8454d88a97aSDan Williams static int count_dimms(struct device *dev, void *c) 8464d88a97aSDan Williams { 8474d88a97aSDan Williams int *count = c; 8484d88a97aSDan Williams 8494d88a97aSDan Williams if (is_nvdimm(dev)) 8504d88a97aSDan Williams (*count)++; 8514d88a97aSDan Williams return 0; 8524d88a97aSDan Williams } 8534d88a97aSDan Williams 8544d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 8554d88a97aSDan Williams { 8564d88a97aSDan Williams int count = 0; 8574d88a97aSDan Williams /* Flush any possible dimm registration failures */ 8584d88a97aSDan Williams nd_synchronize(); 8594d88a97aSDan Williams 8604d88a97aSDan Williams device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 861426824d6SDan Williams dev_dbg(&nvdimm_bus->dev, "count: %d\n", count); 8624d88a97aSDan Williams if (count != dimm_count) 8634d88a97aSDan Williams return -ENXIO; 8644d88a97aSDan Williams return 0; 8654d88a97aSDan Williams } 8664d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 867b354aba0SDan Williams 868b354aba0SDan Williams void __exit nvdimm_devs_exit(void) 869b354aba0SDan Williams { 870b354aba0SDan Williams ida_destroy(&dimm_ida); 871b354aba0SDan Williams } 872