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 21d5d30d5aSDan Williams static bool noblk; 22d5d30d5aSDan Williams module_param(noblk, bool, 0444); 23d5d30d5aSDan Williams MODULE_PARM_DESC(noblk, "force disable BLK / local alias support"); 24d5d30d5aSDan Williams 254d88a97aSDan Williams /* 264d88a97aSDan Williams * Retrieve bus and dimm handle and return if this bus supports 274d88a97aSDan Williams * get_config_data commands 284d88a97aSDan Williams */ 29aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev) 304d88a97aSDan Williams { 31aee65987SToshi Kani struct nvdimm *nvdimm = to_nvdimm(dev); 324d88a97aSDan Williams 33aee65987SToshi Kani if (!nvdimm->cmd_mask || 34aee65987SToshi Kani !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { 358f078b38SDan Williams if (test_bit(NDD_ALIASING, &nvdimm->flags)) 364d88a97aSDan Williams return -ENXIO; 37aee65987SToshi Kani else 38aee65987SToshi Kani return -ENOTTY; 39aee65987SToshi Kani } 404d88a97aSDan Williams 414d88a97aSDan Williams return 0; 424d88a97aSDan Williams } 434d88a97aSDan Williams 444d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd) 454d88a97aSDan Williams { 46aee65987SToshi Kani int rc; 474d88a97aSDan Williams 48aee65987SToshi Kani if (!ndd) 49aee65987SToshi Kani return -EINVAL; 50aee65987SToshi Kani 51aee65987SToshi Kani rc = nvdimm_check_config_data(ndd->dev); 52aee65987SToshi Kani if (rc) 53d75f773cSSakari Ailus dev_dbg(ndd->dev, "%ps: %s error: %d\n", 544d88a97aSDan Williams __builtin_return_address(0), __func__, rc); 554d88a97aSDan Williams return rc; 564d88a97aSDan Williams } 574d88a97aSDan Williams 584d88a97aSDan Williams /** 594d88a97aSDan Williams * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area 604d88a97aSDan Williams * @nvdimm: dimm to initialize 614d88a97aSDan Williams */ 624d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd) 634d88a97aSDan Williams { 644d88a97aSDan Williams struct nd_cmd_get_config_size *cmd = &ndd->nsarea; 654d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 664d88a97aSDan Williams struct nvdimm_bus_descriptor *nd_desc; 674d88a97aSDan Williams int rc = validate_dimm(ndd); 689d62ed96SDan Williams int cmd_rc = 0; 694d88a97aSDan Williams 704d88a97aSDan Williams if (rc) 714d88a97aSDan Williams return rc; 724d88a97aSDan Williams 734d88a97aSDan Williams if (cmd->config_size) 744d88a97aSDan Williams return 0; /* already valid */ 754d88a97aSDan Williams 764d88a97aSDan Williams memset(cmd, 0, sizeof(*cmd)); 774d88a97aSDan Williams nd_desc = nvdimm_bus->nd_desc; 789d62ed96SDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 799d62ed96SDan Williams ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc); 809d62ed96SDan Williams if (rc < 0) 819d62ed96SDan Williams return rc; 829d62ed96SDan Williams return cmd_rc; 834d88a97aSDan Williams } 844d88a97aSDan Williams 852d657d17SAlexander Duyck int nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf, 862d657d17SAlexander Duyck size_t offset, size_t len) 874d88a97aSDan Williams { 884d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 892d657d17SAlexander Duyck struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 90e7c5a571SDan Williams int rc = validate_dimm(ndd), cmd_rc = 0; 914d88a97aSDan Williams struct nd_cmd_get_config_data_hdr *cmd; 922d657d17SAlexander Duyck size_t max_cmd_size, buf_offset; 934d88a97aSDan Williams 944d88a97aSDan Williams if (rc) 954d88a97aSDan Williams return rc; 964d88a97aSDan Williams 972d657d17SAlexander Duyck if (offset + len > ndd->nsarea.config_size) 984d88a97aSDan Williams return -ENXIO; 994d88a97aSDan Williams 1002d657d17SAlexander Duyck max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 101d11cf4a7SDan Williams cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL); 1024d88a97aSDan Williams if (!cmd) 1034d88a97aSDan Williams return -ENOMEM; 1044d88a97aSDan Williams 1052d657d17SAlexander Duyck for (buf_offset = 0; len; 1062d657d17SAlexander Duyck len -= cmd->in_length, buf_offset += cmd->in_length) { 1072d657d17SAlexander Duyck size_t cmd_size; 1082d657d17SAlexander Duyck 1092d657d17SAlexander Duyck cmd->in_offset = offset + buf_offset; 1102d657d17SAlexander Duyck cmd->in_length = min(max_cmd_size, len); 1112d657d17SAlexander Duyck 1122d657d17SAlexander Duyck cmd_size = sizeof(*cmd) + cmd->in_length; 1132d657d17SAlexander Duyck 1144d88a97aSDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 1152d657d17SAlexander Duyck ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 116e7c5a571SDan Williams if (rc < 0) 117e7c5a571SDan Williams break; 118e7c5a571SDan Williams if (cmd_rc < 0) { 119e7c5a571SDan Williams rc = cmd_rc; 1204d88a97aSDan Williams break; 1214d88a97aSDan Williams } 1222d657d17SAlexander Duyck 1232d657d17SAlexander Duyck /* out_buf should be valid, copy it into our output buffer */ 1242d657d17SAlexander Duyck memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length); 1254d88a97aSDan Williams } 126d11cf4a7SDan Williams kvfree(cmd); 1274d88a97aSDan Williams 1284d88a97aSDan Williams return rc; 1294d88a97aSDan Williams } 1304d88a97aSDan Williams 131f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, 132f524bf27SDan Williams void *buf, size_t len) 133f524bf27SDan Williams { 134f524bf27SDan Williams size_t max_cmd_size, buf_offset; 135f524bf27SDan Williams struct nd_cmd_set_config_hdr *cmd; 136e7c5a571SDan Williams int rc = validate_dimm(ndd), cmd_rc = 0; 137f524bf27SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 138f524bf27SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 139f524bf27SDan Williams 140f524bf27SDan Williams if (rc) 141f524bf27SDan Williams return rc; 142f524bf27SDan Williams 143f524bf27SDan Williams if (offset + len > ndd->nsarea.config_size) 144f524bf27SDan Williams return -ENXIO; 145f524bf27SDan Williams 146d11cf4a7SDan Williams max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 147d11cf4a7SDan Williams cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL); 148f524bf27SDan Williams if (!cmd) 149f524bf27SDan Williams return -ENOMEM; 150f524bf27SDan Williams 151f524bf27SDan Williams for (buf_offset = 0; len; len -= cmd->in_length, 152f524bf27SDan Williams buf_offset += cmd->in_length) { 153f524bf27SDan Williams size_t cmd_size; 154f524bf27SDan Williams 155f524bf27SDan Williams cmd->in_offset = offset + buf_offset; 156f524bf27SDan Williams cmd->in_length = min(max_cmd_size, len); 157f524bf27SDan Williams memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length); 158f524bf27SDan Williams 159f524bf27SDan Williams /* status is output in the last 4-bytes of the command buffer */ 160f524bf27SDan Williams cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32); 161f524bf27SDan Williams 162f524bf27SDan Williams rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 163e7c5a571SDan Williams ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 164e7c5a571SDan Williams if (rc < 0) 165e7c5a571SDan Williams break; 166e7c5a571SDan Williams if (cmd_rc < 0) { 167e7c5a571SDan Williams rc = cmd_rc; 168f524bf27SDan Williams break; 169f524bf27SDan Williams } 170f524bf27SDan Williams } 171d11cf4a7SDan Williams kvfree(cmd); 172f524bf27SDan Williams 173f524bf27SDan Williams return rc; 174f524bf27SDan Williams } 175f524bf27SDan Williams 17642237e39SDan Williams void nvdimm_set_aliasing(struct device *dev) 17742237e39SDan Williams { 17842237e39SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 17942237e39SDan Williams 1808f078b38SDan Williams set_bit(NDD_ALIASING, &nvdimm->flags); 1818f078b38SDan Williams } 1828f078b38SDan Williams 1838f078b38SDan Williams void nvdimm_set_locked(struct device *dev) 1848f078b38SDan Williams { 1858f078b38SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 1868f078b38SDan Williams 1878f078b38SDan Williams set_bit(NDD_LOCKED, &nvdimm->flags); 18842237e39SDan Williams } 18942237e39SDan Williams 190d34cb808SDan Williams void nvdimm_clear_locked(struct device *dev) 191d34cb808SDan Williams { 192d34cb808SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 193d34cb808SDan Williams 194d34cb808SDan Williams clear_bit(NDD_LOCKED, &nvdimm->flags); 195d34cb808SDan Williams } 196d34cb808SDan Williams 197e6dfb2deSDan Williams static void nvdimm_release(struct device *dev) 198e6dfb2deSDan Williams { 199e6dfb2deSDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 200e6dfb2deSDan Williams 201e6dfb2deSDan Williams ida_simple_remove(&dimm_ida, nvdimm->id); 202e6dfb2deSDan Williams kfree(nvdimm); 203e6dfb2deSDan Williams } 204e6dfb2deSDan Williams 205e6dfb2deSDan Williams static struct device_type nvdimm_device_type = { 206e6dfb2deSDan Williams .name = "nvdimm", 207e6dfb2deSDan Williams .release = nvdimm_release, 208e6dfb2deSDan Williams }; 209e6dfb2deSDan Williams 21062232e45SDan Williams bool is_nvdimm(struct device *dev) 211e6dfb2deSDan Williams { 212e6dfb2deSDan Williams return dev->type == &nvdimm_device_type; 213e6dfb2deSDan Williams } 214e6dfb2deSDan Williams 215e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev) 216e6dfb2deSDan Williams { 217e6dfb2deSDan Williams struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); 218e6dfb2deSDan Williams 219e6dfb2deSDan Williams WARN_ON(!is_nvdimm(dev)); 220e6dfb2deSDan Williams return nvdimm; 221e6dfb2deSDan Williams } 222e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm); 223e6dfb2deSDan Williams 224047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr) 225047fc8a1SRoss Zwisler { 226047fc8a1SRoss Zwisler struct nd_region *nd_region = &ndbr->nd_region; 227047fc8a1SRoss Zwisler struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 228047fc8a1SRoss Zwisler 229047fc8a1SRoss Zwisler return nd_mapping->nvdimm; 230047fc8a1SRoss Zwisler } 231047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm); 232047fc8a1SRoss Zwisler 233ca6a4657SDan Williams unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr) 234ca6a4657SDan Williams { 235ca6a4657SDan Williams /* pmem mapping properties are private to libnvdimm */ 236ca6a4657SDan Williams return ARCH_MEMREMAP_PMEM; 237ca6a4657SDan Williams } 238ca6a4657SDan Williams EXPORT_SYMBOL_GPL(nd_blk_memremap_flags); 239ca6a4657SDan Williams 240bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping) 241bf9bccc1SDan Williams { 242bf9bccc1SDan Williams struct nvdimm *nvdimm = nd_mapping->nvdimm; 243bf9bccc1SDan Williams 244bf9bccc1SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 245bf9bccc1SDan Williams 246bf9bccc1SDan Williams return dev_get_drvdata(&nvdimm->dev); 247bf9bccc1SDan Williams } 248bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd); 249bf9bccc1SDan Williams 250bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref) 251bf9bccc1SDan Williams { 252bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref); 253bf9bccc1SDan Williams struct device *dev = ndd->dev; 254bf9bccc1SDan Williams struct resource *res, *_r; 255bf9bccc1SDan Williams 256426824d6SDan Williams dev_dbg(dev, "trace\n"); 257bf9bccc1SDan Williams nvdimm_bus_lock(dev); 258bf9bccc1SDan Williams for_each_dpa_resource_safe(ndd, res, _r) 259bf9bccc1SDan Williams nvdimm_free_dpa(ndd, res); 260bf9bccc1SDan Williams nvdimm_bus_unlock(dev); 261bf9bccc1SDan Williams 262a06a7576Syalin wang kvfree(ndd->data); 263bf9bccc1SDan Williams kfree(ndd); 264bf9bccc1SDan Williams put_device(dev); 265bf9bccc1SDan Williams } 266bf9bccc1SDan Williams 267bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd) 268bf9bccc1SDan Williams { 269bf9bccc1SDan Williams kref_get(&ndd->kref); 270bf9bccc1SDan Williams } 271bf9bccc1SDan Williams 272bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd) 273bf9bccc1SDan Williams { 274bf9bccc1SDan Williams if (ndd) 275bf9bccc1SDan Williams kref_put(&ndd->kref, nvdimm_drvdata_release); 276bf9bccc1SDan Williams } 277bf9bccc1SDan Williams 278e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm) 279e6dfb2deSDan Williams { 280e6dfb2deSDan Williams return dev_name(&nvdimm->dev); 281e6dfb2deSDan Williams } 282e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name); 283e6dfb2deSDan Williams 284ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) 285ba9c8dd3SDan Williams { 286ba9c8dd3SDan Williams return &nvdimm->dev.kobj; 287ba9c8dd3SDan Williams } 288ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj); 289ba9c8dd3SDan Williams 290e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) 291e3654ecaSDan Williams { 292e3654ecaSDan Williams return nvdimm->cmd_mask; 293e3654ecaSDan Williams } 294e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask); 295e3654ecaSDan Williams 296e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm) 297e6dfb2deSDan Williams { 29862232e45SDan Williams if (nvdimm) 299e6dfb2deSDan Williams return nvdimm->provider_data; 30062232e45SDan Williams return NULL; 301e6dfb2deSDan Williams } 302e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data); 303e6dfb2deSDan Williams 30462232e45SDan Williams static ssize_t commands_show(struct device *dev, 30562232e45SDan Williams struct device_attribute *attr, char *buf) 30662232e45SDan Williams { 30762232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 30862232e45SDan Williams int cmd, len = 0; 30962232e45SDan Williams 310e3654ecaSDan Williams if (!nvdimm->cmd_mask) 31162232e45SDan Williams return sprintf(buf, "\n"); 31262232e45SDan Williams 313e3654ecaSDan Williams for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) 31462232e45SDan Williams len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); 31562232e45SDan Williams len += sprintf(buf + len, "\n"); 31662232e45SDan Williams return len; 31762232e45SDan Williams } 31862232e45SDan Williams static DEVICE_ATTR_RO(commands); 31962232e45SDan Williams 320efbf6f50SDan Williams static ssize_t flags_show(struct device *dev, 321efbf6f50SDan Williams struct device_attribute *attr, char *buf) 322efbf6f50SDan Williams { 323efbf6f50SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 324efbf6f50SDan Williams 325efbf6f50SDan Williams return sprintf(buf, "%s%s\n", 326efbf6f50SDan Williams test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "", 327efbf6f50SDan Williams test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : ""); 328efbf6f50SDan Williams } 329efbf6f50SDan Williams static DEVICE_ATTR_RO(flags); 330efbf6f50SDan Williams 331eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr, 332eaf96153SDan Williams char *buf) 333eaf96153SDan Williams { 334eaf96153SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 335eaf96153SDan Williams 336eaf96153SDan Williams /* 337eaf96153SDan Williams * The state may be in the process of changing, userspace should 338eaf96153SDan Williams * quiesce probing if it wants a static answer 339eaf96153SDan Williams */ 340eaf96153SDan Williams nvdimm_bus_lock(dev); 341eaf96153SDan Williams nvdimm_bus_unlock(dev); 342eaf96153SDan Williams return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) 343eaf96153SDan Williams ? "active" : "idle"); 344eaf96153SDan Williams } 345eaf96153SDan Williams static DEVICE_ATTR_RO(state); 346eaf96153SDan Williams 3470ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev, 3480ba1c634SDan Williams struct device_attribute *attr, char *buf) 3490ba1c634SDan Williams { 3500ba1c634SDan Williams struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); 3510ba1c634SDan Williams ssize_t rc; 3520ba1c634SDan Williams u32 nfree; 3530ba1c634SDan Williams 3540ba1c634SDan Williams if (!ndd) 3550ba1c634SDan Williams return -ENXIO; 3560ba1c634SDan Williams 3570ba1c634SDan Williams nvdimm_bus_lock(dev); 3580ba1c634SDan Williams nfree = nd_label_nfree(ndd); 3590ba1c634SDan Williams if (nfree - 1 > nfree) { 3600ba1c634SDan Williams dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); 3610ba1c634SDan Williams nfree = 0; 3620ba1c634SDan Williams } else 3630ba1c634SDan Williams nfree--; 3640ba1c634SDan Williams rc = sprintf(buf, "%d\n", nfree); 3650ba1c634SDan Williams nvdimm_bus_unlock(dev); 3660ba1c634SDan Williams return rc; 3670ba1c634SDan Williams } 3680ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots); 3690ba1c634SDan Williams 3703c13e2acSDave Jiang __weak ssize_t security_show(struct device *dev, 371f2989396SDave Jiang struct device_attribute *attr, char *buf) 372f2989396SDave Jiang { 373f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 374f2989396SDave Jiang 375f2989396SDave Jiang switch (nvdimm->sec.state) { 376f2989396SDave Jiang case NVDIMM_SECURITY_DISABLED: 377f2989396SDave Jiang return sprintf(buf, "disabled\n"); 378f2989396SDave Jiang case NVDIMM_SECURITY_UNLOCKED: 379f2989396SDave Jiang return sprintf(buf, "unlocked\n"); 380f2989396SDave Jiang case NVDIMM_SECURITY_LOCKED: 381f2989396SDave Jiang return sprintf(buf, "locked\n"); 382f2989396SDave Jiang case NVDIMM_SECURITY_FROZEN: 383f2989396SDave Jiang return sprintf(buf, "frozen\n"); 384f2989396SDave Jiang case NVDIMM_SECURITY_OVERWRITE: 385f2989396SDave Jiang return sprintf(buf, "overwrite\n"); 38689fa9d8eSDave Jiang default: 38789fa9d8eSDave Jiang return -ENOTTY; 388f2989396SDave Jiang } 389f2989396SDave Jiang 390f2989396SDave Jiang return -ENOTTY; 391f2989396SDave Jiang } 39237833fb7SDave Jiang 39303b65b22SDave Jiang #define OPS \ 39403b65b22SDave Jiang C( OP_FREEZE, "freeze", 1), \ 395d2a4ac73SDave Jiang C( OP_DISABLE, "disable", 2), \ 39664e77c8cSDave Jiang C( OP_UPDATE, "update", 3), \ 3977d988097SDave Jiang C( OP_ERASE, "erase", 2), \ 39889fa9d8eSDave Jiang C( OP_OVERWRITE, "overwrite", 2), \ 39989fa9d8eSDave Jiang C( OP_MASTER_UPDATE, "master_update", 3), \ 40089fa9d8eSDave Jiang C( OP_MASTER_ERASE, "master_erase", 2) 40103b65b22SDave Jiang #undef C 40203b65b22SDave Jiang #define C(a, b, c) a 40303b65b22SDave Jiang enum nvdimmsec_op_ids { OPS }; 40403b65b22SDave Jiang #undef C 40503b65b22SDave Jiang #define C(a, b, c) { b, c } 40603b65b22SDave Jiang static struct { 40703b65b22SDave Jiang const char *name; 40803b65b22SDave Jiang int args; 40903b65b22SDave Jiang } ops[] = { OPS }; 41003b65b22SDave Jiang #undef C 41103b65b22SDave Jiang 41203b65b22SDave Jiang #define SEC_CMD_SIZE 32 41303b65b22SDave Jiang #define KEY_ID_SIZE 10 41403b65b22SDave Jiang 41537833fb7SDave Jiang static ssize_t __security_store(struct device *dev, const char *buf, size_t len) 41637833fb7SDave Jiang { 41737833fb7SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 41837833fb7SDave Jiang ssize_t rc; 41903b65b22SDave Jiang char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1], 42003b65b22SDave Jiang nkeystr[KEY_ID_SIZE+1]; 42103b65b22SDave Jiang unsigned int key, newkey; 42203b65b22SDave Jiang int i; 42337833fb7SDave Jiang 42437833fb7SDave Jiang if (atomic_read(&nvdimm->busy)) 42537833fb7SDave Jiang return -EBUSY; 42637833fb7SDave Jiang 42703b65b22SDave Jiang rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s" 42803b65b22SDave Jiang " %"__stringify(KEY_ID_SIZE)"s" 42903b65b22SDave Jiang " %"__stringify(KEY_ID_SIZE)"s", 43003b65b22SDave Jiang cmd, keystr, nkeystr); 43103b65b22SDave Jiang if (rc < 1) 43203b65b22SDave Jiang return -EINVAL; 43303b65b22SDave Jiang for (i = 0; i < ARRAY_SIZE(ops); i++) 43403b65b22SDave Jiang if (sysfs_streq(cmd, ops[i].name)) 43503b65b22SDave Jiang break; 43603b65b22SDave Jiang if (i >= ARRAY_SIZE(ops)) 43703b65b22SDave Jiang return -EINVAL; 43803b65b22SDave Jiang if (ops[i].args > 1) 43903b65b22SDave Jiang rc = kstrtouint(keystr, 0, &key); 44003b65b22SDave Jiang if (rc >= 0 && ops[i].args > 2) 44103b65b22SDave Jiang rc = kstrtouint(nkeystr, 0, &newkey); 44203b65b22SDave Jiang if (rc < 0) 44303b65b22SDave Jiang return rc; 44403b65b22SDave Jiang 44503b65b22SDave Jiang if (i == OP_FREEZE) { 44637833fb7SDave Jiang dev_dbg(dev, "freeze\n"); 44737833fb7SDave Jiang rc = nvdimm_security_freeze(nvdimm); 44803b65b22SDave Jiang } else if (i == OP_DISABLE) { 44903b65b22SDave Jiang dev_dbg(dev, "disable %u\n", key); 45003b65b22SDave Jiang rc = nvdimm_security_disable(nvdimm, key); 451d2a4ac73SDave Jiang } else if (i == OP_UPDATE) { 452d2a4ac73SDave Jiang dev_dbg(dev, "update %u %u\n", key, newkey); 45389fa9d8eSDave Jiang rc = nvdimm_security_update(nvdimm, key, newkey, NVDIMM_USER); 45464e77c8cSDave Jiang } else if (i == OP_ERASE) { 45564e77c8cSDave Jiang dev_dbg(dev, "erase %u\n", key); 45689fa9d8eSDave Jiang rc = nvdimm_security_erase(nvdimm, key, NVDIMM_USER); 4577d988097SDave Jiang } else if (i == OP_OVERWRITE) { 4587d988097SDave Jiang dev_dbg(dev, "overwrite %u\n", key); 4597d988097SDave Jiang rc = nvdimm_security_overwrite(nvdimm, key); 46089fa9d8eSDave Jiang } else if (i == OP_MASTER_UPDATE) { 46189fa9d8eSDave Jiang dev_dbg(dev, "master_update %u %u\n", key, newkey); 46289fa9d8eSDave Jiang rc = nvdimm_security_update(nvdimm, key, newkey, 46389fa9d8eSDave Jiang NVDIMM_MASTER); 46489fa9d8eSDave Jiang } else if (i == OP_MASTER_ERASE) { 46589fa9d8eSDave Jiang dev_dbg(dev, "master_erase %u\n", key); 46689fa9d8eSDave Jiang rc = nvdimm_security_erase(nvdimm, key, 46789fa9d8eSDave Jiang NVDIMM_MASTER); 46837833fb7SDave Jiang } else 46937833fb7SDave Jiang return -EINVAL; 47037833fb7SDave Jiang 47137833fb7SDave Jiang if (rc == 0) 47237833fb7SDave Jiang rc = len; 47337833fb7SDave Jiang return rc; 47437833fb7SDave Jiang } 47537833fb7SDave Jiang 47637833fb7SDave Jiang static ssize_t security_store(struct device *dev, 47737833fb7SDave Jiang struct device_attribute *attr, const char *buf, size_t len) 47837833fb7SDave Jiang 47937833fb7SDave Jiang { 48037833fb7SDave Jiang ssize_t rc; 48137833fb7SDave Jiang 48237833fb7SDave Jiang /* 48337833fb7SDave Jiang * Require all userspace triggered security management to be 48437833fb7SDave Jiang * done while probing is idle and the DIMM is not in active use 48537833fb7SDave Jiang * in any region. 48637833fb7SDave Jiang */ 48737833fb7SDave Jiang device_lock(dev); 48837833fb7SDave Jiang nvdimm_bus_lock(dev); 48937833fb7SDave Jiang wait_nvdimm_bus_probe_idle(dev); 49037833fb7SDave Jiang rc = __security_store(dev, buf, len); 49137833fb7SDave Jiang nvdimm_bus_unlock(dev); 49237833fb7SDave Jiang device_unlock(dev); 49337833fb7SDave Jiang 49437833fb7SDave Jiang return rc; 49537833fb7SDave Jiang } 49637833fb7SDave Jiang static DEVICE_ATTR_RW(security); 497f2989396SDave Jiang 49862232e45SDan Williams static struct attribute *nvdimm_attributes[] = { 499eaf96153SDan Williams &dev_attr_state.attr, 500efbf6f50SDan Williams &dev_attr_flags.attr, 50162232e45SDan Williams &dev_attr_commands.attr, 5020ba1c634SDan Williams &dev_attr_available_slots.attr, 503f2989396SDave Jiang &dev_attr_security.attr, 50462232e45SDan Williams NULL, 50562232e45SDan Williams }; 50662232e45SDan Williams 507f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) 508f2989396SDave Jiang { 509f2989396SDave Jiang struct device *dev = container_of(kobj, typeof(*dev), kobj); 510f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 511f2989396SDave Jiang 512f2989396SDave Jiang if (a != &dev_attr_security.attr) 513f2989396SDave Jiang return a->mode; 514f2989396SDave Jiang if (nvdimm->sec.state < 0) 515f2989396SDave Jiang return 0; 51637833fb7SDave Jiang /* Are there any state mutation ops? */ 517d2a4ac73SDave Jiang if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable 51864e77c8cSDave Jiang || nvdimm->sec.ops->change_key 5197d988097SDave Jiang || nvdimm->sec.ops->erase 5207d988097SDave Jiang || nvdimm->sec.ops->overwrite) 521f2989396SDave Jiang return a->mode; 52237833fb7SDave Jiang return 0444; 523f2989396SDave Jiang } 524f2989396SDave Jiang 52562232e45SDan Williams struct attribute_group nvdimm_attribute_group = { 52662232e45SDan Williams .attrs = nvdimm_attributes, 527f2989396SDave Jiang .is_visible = nvdimm_visible, 52862232e45SDan Williams }; 52962232e45SDan Williams EXPORT_SYMBOL_GPL(nvdimm_attribute_group); 53062232e45SDan Williams 531d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, 532d6548ae4SDave Jiang void *provider_data, const struct attribute_group **groups, 533d6548ae4SDave Jiang unsigned long flags, unsigned long cmd_mask, int num_flush, 534f2989396SDave Jiang struct resource *flush_wpq, const char *dimm_id, 535f2989396SDave Jiang const struct nvdimm_security_ops *sec_ops) 536e6dfb2deSDan Williams { 537e6dfb2deSDan Williams struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 538e6dfb2deSDan Williams struct device *dev; 539e6dfb2deSDan Williams 540e6dfb2deSDan Williams if (!nvdimm) 541e6dfb2deSDan Williams return NULL; 542e6dfb2deSDan Williams 543e6dfb2deSDan Williams nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); 544e6dfb2deSDan Williams if (nvdimm->id < 0) { 545e6dfb2deSDan Williams kfree(nvdimm); 546e6dfb2deSDan Williams return NULL; 547e6dfb2deSDan Williams } 548d6548ae4SDave Jiang 549d6548ae4SDave Jiang nvdimm->dimm_id = dimm_id; 550e6dfb2deSDan Williams nvdimm->provider_data = provider_data; 551d5d30d5aSDan Williams if (noblk) 552d5d30d5aSDan Williams flags |= 1 << NDD_NOBLK; 553e6dfb2deSDan Williams nvdimm->flags = flags; 554e3654ecaSDan Williams nvdimm->cmd_mask = cmd_mask; 555e5ae3b25SDan Williams nvdimm->num_flush = num_flush; 556e5ae3b25SDan Williams nvdimm->flush_wpq = flush_wpq; 557eaf96153SDan Williams atomic_set(&nvdimm->busy, 0); 558e6dfb2deSDan Williams dev = &nvdimm->dev; 559e6dfb2deSDan Williams dev_set_name(dev, "nmem%d", nvdimm->id); 560e6dfb2deSDan Williams dev->parent = &nvdimm_bus->dev; 561e6dfb2deSDan Williams dev->type = &nvdimm_device_type; 56262232e45SDan Williams dev->devt = MKDEV(nvdimm_major, nvdimm->id); 563e6dfb2deSDan Williams dev->groups = groups; 564f2989396SDave Jiang nvdimm->sec.ops = sec_ops; 5657d988097SDave Jiang nvdimm->sec.overwrite_tmo = 0; 5667d988097SDave Jiang INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query); 567f2989396SDave Jiang /* 568f2989396SDave Jiang * Security state must be initialized before device_add() for 569f2989396SDave Jiang * attribute visibility. 570f2989396SDave Jiang */ 57189fa9d8eSDave Jiang /* get security state and extended (master) state */ 57289fa9d8eSDave Jiang nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); 57389fa9d8eSDave Jiang nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER); 5744d88a97aSDan Williams nd_device_register(dev); 575e6dfb2deSDan Williams 576e6dfb2deSDan Williams return nvdimm; 577e6dfb2deSDan Williams } 578d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create); 5794d88a97aSDan Williams 5801cd73865SDan Williams static void shutdown_security_notify(void *data) 5817d988097SDave Jiang { 5821cd73865SDan Williams struct nvdimm *nvdimm = data; 5831cd73865SDan Williams 5841cd73865SDan Williams sysfs_put(nvdimm->sec.overwrite_state); 5851cd73865SDan Williams } 5861cd73865SDan Williams 5871cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev) 5881cd73865SDan Williams { 5891cd73865SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 5901cd73865SDan Williams 5911cd73865SDan Williams if (nvdimm->sec.state < 0 || !nvdimm->sec.ops 5921cd73865SDan Williams || !nvdimm->sec.ops->overwrite) 5937d988097SDave Jiang return 0; 5941cd73865SDan Williams nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); 5951cd73865SDan Williams if (!nvdimm->sec.overwrite_state) 5961cd73865SDan Williams return -ENOMEM; 5971cd73865SDan Williams 5981cd73865SDan Williams return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm); 5997d988097SDave Jiang } 6007d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events); 6017d988097SDave Jiang 6027d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm) 6037d988097SDave Jiang { 6047d988097SDave Jiang return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 6057d988097SDave Jiang } 6067d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite); 6077d988097SDave Jiang 60837833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm) 60937833fb7SDave Jiang { 61037833fb7SDave Jiang int rc; 61137833fb7SDave Jiang 61237833fb7SDave Jiang WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 61337833fb7SDave Jiang 61437833fb7SDave Jiang if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) 61537833fb7SDave Jiang return -EOPNOTSUPP; 61637833fb7SDave Jiang 61737833fb7SDave Jiang if (nvdimm->sec.state < 0) 61837833fb7SDave Jiang return -EIO; 61937833fb7SDave Jiang 6207d988097SDave Jiang if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 6217d988097SDave Jiang dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n"); 6227d988097SDave Jiang return -EBUSY; 6237d988097SDave Jiang } 6247d988097SDave Jiang 62537833fb7SDave Jiang rc = nvdimm->sec.ops->freeze(nvdimm); 62689fa9d8eSDave Jiang nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); 62737833fb7SDave Jiang 62837833fb7SDave Jiang return rc; 62937833fb7SDave Jiang } 63037833fb7SDave Jiang 631762d067dSDan Williams int alias_dpa_busy(struct device *dev, void *data) 632a1f3e4d6SDan Williams { 633fe514739SDan Williams resource_size_t map_end, blk_start, new; 634a1f3e4d6SDan Williams struct blk_alloc_info *info = data; 635a1f3e4d6SDan Williams struct nd_mapping *nd_mapping; 636a1f3e4d6SDan Williams struct nd_region *nd_region; 637a1f3e4d6SDan Williams struct nvdimm_drvdata *ndd; 638a1f3e4d6SDan Williams struct resource *res; 639a1f3e4d6SDan Williams int i; 640a1f3e4d6SDan Williams 641c9e582aaSDan Williams if (!is_memory(dev)) 642a1f3e4d6SDan Williams return 0; 643a1f3e4d6SDan Williams 644a1f3e4d6SDan Williams nd_region = to_nd_region(dev); 645a1f3e4d6SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 646a1f3e4d6SDan Williams nd_mapping = &nd_region->mapping[i]; 647a1f3e4d6SDan Williams if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) 648a1f3e4d6SDan Williams break; 649a1f3e4d6SDan Williams } 650a1f3e4d6SDan Williams 651a1f3e4d6SDan Williams if (i >= nd_region->ndr_mappings) 652a1f3e4d6SDan Williams return 0; 653a1f3e4d6SDan Williams 654a1f3e4d6SDan Williams ndd = to_ndd(nd_mapping); 655a1f3e4d6SDan Williams map_end = nd_mapping->start + nd_mapping->size - 1; 656a1f3e4d6SDan Williams blk_start = nd_mapping->start; 657762d067dSDan Williams 658762d067dSDan Williams /* 659762d067dSDan Williams * In the allocation case ->res is set to free space that we are 660762d067dSDan Williams * looking to validate against PMEM aliasing collision rules 661762d067dSDan Williams * (i.e. BLK is allocated after all aliased PMEM). 662762d067dSDan Williams */ 663762d067dSDan Williams if (info->res) { 664762d067dSDan Williams if (info->res->start >= nd_mapping->start 665762d067dSDan Williams && info->res->start < map_end) 666762d067dSDan Williams /* pass */; 667762d067dSDan Williams else 668762d067dSDan Williams return 0; 669762d067dSDan Williams } 670762d067dSDan Williams 671a1f3e4d6SDan Williams retry: 672a1f3e4d6SDan Williams /* 673a1f3e4d6SDan Williams * Find the free dpa from the end of the last pmem allocation to 674fe514739SDan Williams * the end of the interleave-set mapping. 675a1f3e4d6SDan Williams */ 676a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 677fe514739SDan Williams if (strncmp(res->name, "pmem", 4) != 0) 678fe514739SDan Williams continue; 679a1f3e4d6SDan Williams if ((res->start >= blk_start && res->start < map_end) 680a1f3e4d6SDan Williams || (res->end >= blk_start 681a1f3e4d6SDan Williams && res->end <= map_end)) { 682fe514739SDan Williams new = max(blk_start, min(map_end + 1, res->end + 1)); 683a1f3e4d6SDan Williams if (new != blk_start) { 684a1f3e4d6SDan Williams blk_start = new; 685a1f3e4d6SDan Williams goto retry; 686a1f3e4d6SDan Williams } 687a1f3e4d6SDan Williams } 688a1f3e4d6SDan Williams } 689a1f3e4d6SDan Williams 690762d067dSDan Williams /* update the free space range with the probed blk_start */ 691762d067dSDan Williams if (info->res && blk_start > info->res->start) { 692762d067dSDan Williams info->res->start = max(info->res->start, blk_start); 693762d067dSDan Williams if (info->res->start > info->res->end) 694762d067dSDan Williams info->res->end = info->res->start - 1; 695762d067dSDan Williams return 1; 696762d067dSDan Williams } 697762d067dSDan Williams 698fe514739SDan Williams info->available -= blk_start - nd_mapping->start; 699762d067dSDan Williams 700a1f3e4d6SDan Williams return 0; 701a1f3e4d6SDan Williams } 702a1f3e4d6SDan Williams 703bf9bccc1SDan Williams /** 7041b40e09aSDan Williams * nd_blk_available_dpa - account the unused dpa of BLK region 7051b40e09aSDan Williams * @nd_mapping: container of dpa-resource-root + labels 7061b40e09aSDan Williams * 707a1f3e4d6SDan Williams * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but 708a1f3e4d6SDan Williams * we arrange for them to never start at an lower dpa than the last 709a1f3e4d6SDan Williams * PMEM allocation in an aliased region. 7101b40e09aSDan Williams */ 711a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) 7121b40e09aSDan Williams { 713a1f3e4d6SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 714a1f3e4d6SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 7151b40e09aSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 716a1f3e4d6SDan Williams struct blk_alloc_info info = { 717a1f3e4d6SDan Williams .nd_mapping = nd_mapping, 718a1f3e4d6SDan Williams .available = nd_mapping->size, 719762d067dSDan Williams .res = NULL, 720a1f3e4d6SDan Williams }; 7211b40e09aSDan Williams struct resource *res; 7221b40e09aSDan Williams 7231b40e09aSDan Williams if (!ndd) 7241b40e09aSDan Williams return 0; 7251b40e09aSDan Williams 726a1f3e4d6SDan Williams device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); 7271b40e09aSDan Williams 728a1f3e4d6SDan Williams /* now account for busy blk allocations in unaliased dpa */ 729a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 730a1f3e4d6SDan Williams if (strncmp(res->name, "blk", 3) != 0) 731a1f3e4d6SDan Williams continue; 732fe514739SDan Williams info.available -= resource_size(res); 7331b40e09aSDan Williams } 7341b40e09aSDan Williams 735a1f3e4d6SDan Williams return info.available; 7361b40e09aSDan Williams } 7371b40e09aSDan Williams 7381b40e09aSDan Williams /** 73912e3129eSKeith Busch * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max 74012e3129eSKeith Busch * contiguous unallocated dpa range. 74112e3129eSKeith Busch * @nd_region: constrain available space check to this reference region 74212e3129eSKeith Busch * @nd_mapping: container of dpa-resource-root + labels 74312e3129eSKeith Busch */ 74412e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region, 74512e3129eSKeith Busch struct nd_mapping *nd_mapping) 74612e3129eSKeith Busch { 74712e3129eSKeith Busch struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 74812e3129eSKeith Busch struct nvdimm_bus *nvdimm_bus; 74912e3129eSKeith Busch resource_size_t max = 0; 75012e3129eSKeith Busch struct resource *res; 75112e3129eSKeith Busch 75212e3129eSKeith Busch /* if a dimm is disabled the available capacity is zero */ 75312e3129eSKeith Busch if (!ndd) 75412e3129eSKeith Busch return 0; 75512e3129eSKeith Busch 75612e3129eSKeith Busch nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 75712e3129eSKeith Busch if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm)) 75812e3129eSKeith Busch return 0; 75912e3129eSKeith Busch for_each_dpa_resource(ndd, res) { 76012e3129eSKeith Busch if (strcmp(res->name, "pmem-reserve") != 0) 76112e3129eSKeith Busch continue; 76212e3129eSKeith Busch if (resource_size(res) > max) 76312e3129eSKeith Busch max = resource_size(res); 76412e3129eSKeith Busch } 76512e3129eSKeith Busch release_free_pmem(nvdimm_bus, nd_mapping); 76612e3129eSKeith Busch return max; 76712e3129eSKeith Busch } 76812e3129eSKeith Busch 76912e3129eSKeith Busch /** 770bf9bccc1SDan Williams * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 771bf9bccc1SDan Williams * @nd_mapping: container of dpa-resource-root + labels 772bf9bccc1SDan Williams * @nd_region: constrain available space check to this reference region 773bf9bccc1SDan Williams * @overlap: calculate available space assuming this level of overlap 774bf9bccc1SDan Williams * 775bf9bccc1SDan Williams * Validate that a PMEM label, if present, aligns with the start of an 776bf9bccc1SDan Williams * interleave set and truncate the available size at the lowest BLK 777bf9bccc1SDan Williams * overlap point. 778bf9bccc1SDan Williams * 779bf9bccc1SDan Williams * The expectation is that this routine is called multiple times as it 780bf9bccc1SDan Williams * probes for the largest BLK encroachment for any single member DIMM of 781bf9bccc1SDan Williams * the interleave set. Once that value is determined the PMEM-limit for 782bf9bccc1SDan Williams * the set can be established. 783bf9bccc1SDan Williams */ 784bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 785bf9bccc1SDan Williams struct nd_mapping *nd_mapping, resource_size_t *overlap) 786bf9bccc1SDan Williams { 787bf9bccc1SDan Williams resource_size_t map_start, map_end, busy = 0, available, blk_start; 788bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 789bf9bccc1SDan Williams struct resource *res; 790bf9bccc1SDan Williams const char *reason; 791bf9bccc1SDan Williams 792bf9bccc1SDan Williams if (!ndd) 793bf9bccc1SDan Williams return 0; 794bf9bccc1SDan Williams 795bf9bccc1SDan Williams map_start = nd_mapping->start; 796bf9bccc1SDan Williams map_end = map_start + nd_mapping->size - 1; 797bf9bccc1SDan Williams blk_start = max(map_start, map_end + 1 - *overlap); 798a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 799bf9bccc1SDan Williams if (res->start >= map_start && res->start < map_end) { 800bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) 801a1f3e4d6SDan Williams blk_start = min(blk_start, 802a1f3e4d6SDan Williams max(map_start, res->start)); 803a1f3e4d6SDan Williams else if (res->end > map_end) { 804bf9bccc1SDan Williams reason = "misaligned to iset"; 805bf9bccc1SDan Williams goto err; 806a1f3e4d6SDan Williams } else 807bf9bccc1SDan Williams busy += resource_size(res); 808bf9bccc1SDan Williams } else if (res->end >= map_start && res->end <= map_end) { 809bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) { 810bf9bccc1SDan Williams /* 811bf9bccc1SDan Williams * If a BLK allocation overlaps the start of 812bf9bccc1SDan Williams * PMEM the entire interleave set may now only 813bf9bccc1SDan Williams * be used for BLK. 814bf9bccc1SDan Williams */ 815bf9bccc1SDan Williams blk_start = map_start; 816a1f3e4d6SDan Williams } else 817a1f3e4d6SDan Williams busy += resource_size(res); 818bf9bccc1SDan Williams } else if (map_start > res->start && map_start < res->end) { 819bf9bccc1SDan Williams /* total eclipse of the mapping */ 820bf9bccc1SDan Williams busy += nd_mapping->size; 821bf9bccc1SDan Williams blk_start = map_start; 822bf9bccc1SDan Williams } 823a1f3e4d6SDan Williams } 824bf9bccc1SDan Williams 825bf9bccc1SDan Williams *overlap = map_end + 1 - blk_start; 826bf9bccc1SDan Williams available = blk_start - map_start; 827bf9bccc1SDan Williams if (busy < available) 828bf9bccc1SDan Williams return available - busy; 829bf9bccc1SDan Williams return 0; 830bf9bccc1SDan Williams 831bf9bccc1SDan Williams err: 832bf9bccc1SDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); 833bf9bccc1SDan Williams return 0; 834bf9bccc1SDan Williams } 835bf9bccc1SDan Williams 8364a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 8374a826c83SDan Williams { 8384a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8394a826c83SDan Williams kfree(res->name); 8404a826c83SDan Williams __release_region(&ndd->dpa, res->start, resource_size(res)); 8414a826c83SDan Williams } 8424a826c83SDan Williams 8434a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 8444a826c83SDan Williams struct nd_label_id *label_id, resource_size_t start, 8454a826c83SDan Williams resource_size_t n) 8464a826c83SDan Williams { 8474a826c83SDan Williams char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 8484a826c83SDan Williams struct resource *res; 8494a826c83SDan Williams 8504a826c83SDan Williams if (!name) 8514a826c83SDan Williams return NULL; 8524a826c83SDan Williams 8534a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8544a826c83SDan Williams res = __request_region(&ndd->dpa, start, n, name, 0); 8554a826c83SDan Williams if (!res) 8564a826c83SDan Williams kfree(name); 8574a826c83SDan Williams return res; 8584a826c83SDan Williams } 8594a826c83SDan Williams 860bf9bccc1SDan Williams /** 861bf9bccc1SDan Williams * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 862bf9bccc1SDan Williams * @nvdimm: container of dpa-resource-root + labels 863bf9bccc1SDan Williams * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid> 864bf9bccc1SDan Williams */ 865bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 866bf9bccc1SDan Williams struct nd_label_id *label_id) 867bf9bccc1SDan Williams { 868bf9bccc1SDan Williams resource_size_t allocated = 0; 869bf9bccc1SDan Williams struct resource *res; 870bf9bccc1SDan Williams 871bf9bccc1SDan Williams for_each_dpa_resource(ndd, res) 872bf9bccc1SDan Williams if (strcmp(res->name, label_id->id) == 0) 873bf9bccc1SDan Williams allocated += resource_size(res); 874bf9bccc1SDan Williams 875bf9bccc1SDan Williams return allocated; 876bf9bccc1SDan Williams } 877bf9bccc1SDan Williams 8784d88a97aSDan Williams static int count_dimms(struct device *dev, void *c) 8794d88a97aSDan Williams { 8804d88a97aSDan Williams int *count = c; 8814d88a97aSDan Williams 8824d88a97aSDan Williams if (is_nvdimm(dev)) 8834d88a97aSDan Williams (*count)++; 8844d88a97aSDan Williams return 0; 8854d88a97aSDan Williams } 8864d88a97aSDan Williams 8874d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 8884d88a97aSDan Williams { 8894d88a97aSDan Williams int count = 0; 8904d88a97aSDan Williams /* Flush any possible dimm registration failures */ 8914d88a97aSDan Williams nd_synchronize(); 8924d88a97aSDan Williams 8934d88a97aSDan Williams device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 894426824d6SDan Williams dev_dbg(&nvdimm_bus->dev, "count: %d\n", count); 8954d88a97aSDan Williams if (count != dimm_count) 8964d88a97aSDan Williams return -ENXIO; 8974d88a97aSDan Williams return 0; 8984d88a97aSDan Williams } 8994d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 900b354aba0SDan Williams 901b354aba0SDan Williams void __exit nvdimm_devs_exit(void) 902b354aba0SDan Williams { 903b354aba0SDan Williams ida_destroy(&dimm_ida); 904b354aba0SDan Williams } 905