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)) { 35a0e37452SDan Williams if (test_bit(NDD_LABELING, &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 176a0e37452SDan Williams void nvdimm_set_labeling(struct device *dev) 17742237e39SDan Williams { 17842237e39SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 17942237e39SDan Williams 180a0e37452SDan Williams set_bit(NDD_LABELING, &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 struct nvdimm *to_nvdimm(struct device *dev) 206e6dfb2deSDan Williams { 207e6dfb2deSDan Williams struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); 208e6dfb2deSDan Williams 209e6dfb2deSDan Williams WARN_ON(!is_nvdimm(dev)); 210e6dfb2deSDan Williams return nvdimm; 211e6dfb2deSDan Williams } 212e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm); 213e6dfb2deSDan Williams 214047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr) 215047fc8a1SRoss Zwisler { 216047fc8a1SRoss Zwisler struct nd_region *nd_region = &ndbr->nd_region; 217047fc8a1SRoss Zwisler struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 218047fc8a1SRoss Zwisler 219047fc8a1SRoss Zwisler return nd_mapping->nvdimm; 220047fc8a1SRoss Zwisler } 221047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm); 222047fc8a1SRoss Zwisler 223ca6a4657SDan Williams unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr) 224ca6a4657SDan Williams { 225ca6a4657SDan Williams /* pmem mapping properties are private to libnvdimm */ 226ca6a4657SDan Williams return ARCH_MEMREMAP_PMEM; 227ca6a4657SDan Williams } 228ca6a4657SDan Williams EXPORT_SYMBOL_GPL(nd_blk_memremap_flags); 229ca6a4657SDan Williams 230bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping) 231bf9bccc1SDan Williams { 232bf9bccc1SDan Williams struct nvdimm *nvdimm = nd_mapping->nvdimm; 233bf9bccc1SDan Williams 234bf9bccc1SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 235bf9bccc1SDan Williams 236bf9bccc1SDan Williams return dev_get_drvdata(&nvdimm->dev); 237bf9bccc1SDan Williams } 238bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd); 239bf9bccc1SDan Williams 240bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref) 241bf9bccc1SDan Williams { 242bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref); 243bf9bccc1SDan Williams struct device *dev = ndd->dev; 244bf9bccc1SDan Williams struct resource *res, *_r; 245bf9bccc1SDan Williams 246426824d6SDan Williams dev_dbg(dev, "trace\n"); 247bf9bccc1SDan Williams nvdimm_bus_lock(dev); 248bf9bccc1SDan Williams for_each_dpa_resource_safe(ndd, res, _r) 249bf9bccc1SDan Williams nvdimm_free_dpa(ndd, res); 250bf9bccc1SDan Williams nvdimm_bus_unlock(dev); 251bf9bccc1SDan Williams 252a06a7576Syalin wang kvfree(ndd->data); 253bf9bccc1SDan Williams kfree(ndd); 254bf9bccc1SDan Williams put_device(dev); 255bf9bccc1SDan Williams } 256bf9bccc1SDan Williams 257bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd) 258bf9bccc1SDan Williams { 259bf9bccc1SDan Williams kref_get(&ndd->kref); 260bf9bccc1SDan Williams } 261bf9bccc1SDan Williams 262bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd) 263bf9bccc1SDan Williams { 264bf9bccc1SDan Williams if (ndd) 265bf9bccc1SDan Williams kref_put(&ndd->kref, nvdimm_drvdata_release); 266bf9bccc1SDan Williams } 267bf9bccc1SDan Williams 268e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm) 269e6dfb2deSDan Williams { 270e6dfb2deSDan Williams return dev_name(&nvdimm->dev); 271e6dfb2deSDan Williams } 272e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name); 273e6dfb2deSDan Williams 274ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) 275ba9c8dd3SDan Williams { 276ba9c8dd3SDan Williams return &nvdimm->dev.kobj; 277ba9c8dd3SDan Williams } 278ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj); 279ba9c8dd3SDan Williams 280e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) 281e3654ecaSDan Williams { 282e3654ecaSDan Williams return nvdimm->cmd_mask; 283e3654ecaSDan Williams } 284e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask); 285e3654ecaSDan Williams 286e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm) 287e6dfb2deSDan Williams { 28862232e45SDan Williams if (nvdimm) 289e6dfb2deSDan Williams return nvdimm->provider_data; 29062232e45SDan Williams return NULL; 291e6dfb2deSDan Williams } 292e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data); 293e6dfb2deSDan Williams 29462232e45SDan Williams static ssize_t commands_show(struct device *dev, 29562232e45SDan Williams struct device_attribute *attr, char *buf) 29662232e45SDan Williams { 29762232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 29862232e45SDan Williams int cmd, len = 0; 29962232e45SDan Williams 300e3654ecaSDan Williams if (!nvdimm->cmd_mask) 30162232e45SDan Williams return sprintf(buf, "\n"); 30262232e45SDan Williams 303e3654ecaSDan Williams for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) 30462232e45SDan Williams len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); 30562232e45SDan Williams len += sprintf(buf + len, "\n"); 30662232e45SDan Williams return len; 30762232e45SDan Williams } 30862232e45SDan Williams static DEVICE_ATTR_RO(commands); 30962232e45SDan Williams 310efbf6f50SDan Williams static ssize_t flags_show(struct device *dev, 311efbf6f50SDan Williams struct device_attribute *attr, char *buf) 312efbf6f50SDan Williams { 313efbf6f50SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 314efbf6f50SDan Williams 315a0e37452SDan Williams return sprintf(buf, "%s%s%s\n", 316efbf6f50SDan Williams test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "", 317a0e37452SDan Williams test_bit(NDD_LABELING, &nvdimm->flags) ? "label " : "", 318efbf6f50SDan Williams test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : ""); 319efbf6f50SDan Williams } 320efbf6f50SDan Williams static DEVICE_ATTR_RO(flags); 321efbf6f50SDan Williams 322eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr, 323eaf96153SDan Williams char *buf) 324eaf96153SDan Williams { 325eaf96153SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 326eaf96153SDan Williams 327eaf96153SDan Williams /* 328eaf96153SDan Williams * The state may be in the process of changing, userspace should 329eaf96153SDan Williams * quiesce probing if it wants a static answer 330eaf96153SDan Williams */ 331eaf96153SDan Williams nvdimm_bus_lock(dev); 332eaf96153SDan Williams nvdimm_bus_unlock(dev); 333eaf96153SDan Williams return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) 334eaf96153SDan Williams ? "active" : "idle"); 335eaf96153SDan Williams } 336eaf96153SDan Williams static DEVICE_ATTR_RO(state); 337eaf96153SDan Williams 3380ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev, 3390ba1c634SDan Williams struct device_attribute *attr, char *buf) 3400ba1c634SDan Williams { 3410ba1c634SDan Williams struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); 3420ba1c634SDan Williams ssize_t rc; 3430ba1c634SDan Williams u32 nfree; 3440ba1c634SDan Williams 3450ba1c634SDan Williams if (!ndd) 3460ba1c634SDan Williams return -ENXIO; 3470ba1c634SDan Williams 3480ba1c634SDan Williams nvdimm_bus_lock(dev); 3490ba1c634SDan Williams nfree = nd_label_nfree(ndd); 3500ba1c634SDan Williams if (nfree - 1 > nfree) { 3510ba1c634SDan Williams dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); 3520ba1c634SDan Williams nfree = 0; 3530ba1c634SDan Williams } else 3540ba1c634SDan Williams nfree--; 3550ba1c634SDan Williams rc = sprintf(buf, "%d\n", nfree); 3560ba1c634SDan Williams nvdimm_bus_unlock(dev); 3570ba1c634SDan Williams return rc; 3580ba1c634SDan Williams } 3590ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots); 3600ba1c634SDan Williams 3613c13e2acSDave Jiang __weak ssize_t security_show(struct device *dev, 362f2989396SDave Jiang struct device_attribute *attr, char *buf) 363f2989396SDave Jiang { 364f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 365f2989396SDave Jiang 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"); 372d78c620aSDan Williams if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags)) 373f2989396SDave Jiang return sprintf(buf, "overwrite\n"); 37489fa9d8eSDave Jiang return -ENOTTY; 375f2989396SDave Jiang } 376f2989396SDave Jiang 377d78c620aSDan Williams static ssize_t frozen_show(struct device *dev, 378d78c620aSDan Williams struct device_attribute *attr, char *buf) 379d78c620aSDan Williams { 380d78c620aSDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 381d78c620aSDan Williams 382d78c620aSDan Williams return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN, 383d78c620aSDan Williams &nvdimm->sec.flags)); 384f2989396SDave Jiang } 385d78c620aSDan Williams static DEVICE_ATTR_RO(frozen); 38637833fb7SDave Jiang 38737833fb7SDave Jiang static ssize_t security_store(struct device *dev, 38837833fb7SDave Jiang struct device_attribute *attr, const char *buf, size_t len) 38937833fb7SDave Jiang 39037833fb7SDave Jiang { 39137833fb7SDave Jiang ssize_t rc; 39237833fb7SDave Jiang 39337833fb7SDave Jiang /* 39437833fb7SDave Jiang * Require all userspace triggered security management to be 39537833fb7SDave Jiang * done while probing is idle and the DIMM is not in active use 39637833fb7SDave Jiang * in any region. 39737833fb7SDave Jiang */ 39887a30e1fSDan Williams nd_device_lock(dev); 39937833fb7SDave Jiang nvdimm_bus_lock(dev); 40037833fb7SDave Jiang wait_nvdimm_bus_probe_idle(dev); 4017b60422cSDan Williams rc = nvdimm_security_store(dev, buf, len); 40237833fb7SDave Jiang nvdimm_bus_unlock(dev); 40387a30e1fSDan Williams nd_device_unlock(dev); 40437833fb7SDave Jiang 40537833fb7SDave Jiang return rc; 40637833fb7SDave Jiang } 40737833fb7SDave Jiang static DEVICE_ATTR_RW(security); 408f2989396SDave Jiang 40962232e45SDan Williams static struct attribute *nvdimm_attributes[] = { 410eaf96153SDan Williams &dev_attr_state.attr, 411efbf6f50SDan Williams &dev_attr_flags.attr, 41262232e45SDan Williams &dev_attr_commands.attr, 4130ba1c634SDan Williams &dev_attr_available_slots.attr, 414f2989396SDave Jiang &dev_attr_security.attr, 415d78c620aSDan Williams &dev_attr_frozen.attr, 41662232e45SDan Williams NULL, 41762232e45SDan Williams }; 41862232e45SDan Williams 419f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) 420f2989396SDave Jiang { 421f2989396SDave Jiang struct device *dev = container_of(kobj, typeof(*dev), kobj); 422f2989396SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev); 423f2989396SDave Jiang 424d78c620aSDan Williams if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr) 425f2989396SDave Jiang return a->mode; 426d78c620aSDan Williams if (!nvdimm->sec.flags) 427f2989396SDave Jiang return 0; 428d78c620aSDan Williams 429d78c620aSDan Williams if (a == &dev_attr_security.attr) { 430d78c620aSDan Williams /* Are there any state mutation ops (make writable)? */ 431d2a4ac73SDave Jiang if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable 43264e77c8cSDave Jiang || nvdimm->sec.ops->change_key 4337d988097SDave Jiang || nvdimm->sec.ops->erase 4347d988097SDave Jiang || nvdimm->sec.ops->overwrite) 435f2989396SDave Jiang return a->mode; 43637833fb7SDave Jiang return 0444; 437f2989396SDave Jiang } 438f2989396SDave Jiang 439d78c620aSDan Williams if (nvdimm->sec.ops->freeze) 440d78c620aSDan Williams return a->mode; 441d78c620aSDan Williams return 0; 442d78c620aSDan Williams } 443d78c620aSDan Williams 444360eba7eSDan Williams static const struct attribute_group nvdimm_attribute_group = { 44562232e45SDan Williams .attrs = nvdimm_attributes, 446f2989396SDave Jiang .is_visible = nvdimm_visible, 44762232e45SDan Williams }; 448360eba7eSDan Williams 449360eba7eSDan Williams static const struct attribute_group *nvdimm_attribute_groups[] = { 450360eba7eSDan Williams &nd_device_attribute_group, 451360eba7eSDan Williams &nvdimm_attribute_group, 452360eba7eSDan Williams NULL, 453360eba7eSDan Williams }; 454360eba7eSDan Williams 455360eba7eSDan Williams static const struct device_type nvdimm_device_type = { 456360eba7eSDan Williams .name = "nvdimm", 457360eba7eSDan Williams .release = nvdimm_release, 458360eba7eSDan Williams .groups = nvdimm_attribute_groups, 459360eba7eSDan Williams }; 460360eba7eSDan Williams 461360eba7eSDan Williams bool is_nvdimm(struct device *dev) 462360eba7eSDan Williams { 463360eba7eSDan Williams return dev->type == &nvdimm_device_type; 464360eba7eSDan Williams } 46562232e45SDan Williams 466d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, 467d6548ae4SDave Jiang void *provider_data, const struct attribute_group **groups, 468d6548ae4SDave Jiang unsigned long flags, unsigned long cmd_mask, int num_flush, 469f2989396SDave Jiang struct resource *flush_wpq, const char *dimm_id, 470f2989396SDave Jiang const struct nvdimm_security_ops *sec_ops) 471e6dfb2deSDan Williams { 472e6dfb2deSDan Williams struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 473e6dfb2deSDan Williams struct device *dev; 474e6dfb2deSDan Williams 475e6dfb2deSDan Williams if (!nvdimm) 476e6dfb2deSDan Williams return NULL; 477e6dfb2deSDan Williams 478e6dfb2deSDan Williams nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); 479e6dfb2deSDan Williams if (nvdimm->id < 0) { 480e6dfb2deSDan Williams kfree(nvdimm); 481e6dfb2deSDan Williams return NULL; 482e6dfb2deSDan Williams } 483d6548ae4SDave Jiang 484d6548ae4SDave Jiang nvdimm->dimm_id = dimm_id; 485e6dfb2deSDan Williams nvdimm->provider_data = provider_data; 486d5d30d5aSDan Williams if (noblk) 487d5d30d5aSDan Williams flags |= 1 << NDD_NOBLK; 488e6dfb2deSDan Williams nvdimm->flags = flags; 489e3654ecaSDan Williams nvdimm->cmd_mask = cmd_mask; 490e5ae3b25SDan Williams nvdimm->num_flush = num_flush; 491e5ae3b25SDan Williams nvdimm->flush_wpq = flush_wpq; 492eaf96153SDan Williams atomic_set(&nvdimm->busy, 0); 493e6dfb2deSDan Williams dev = &nvdimm->dev; 494e6dfb2deSDan Williams dev_set_name(dev, "nmem%d", nvdimm->id); 495e6dfb2deSDan Williams dev->parent = &nvdimm_bus->dev; 496e6dfb2deSDan Williams dev->type = &nvdimm_device_type; 49762232e45SDan Williams dev->devt = MKDEV(nvdimm_major, nvdimm->id); 498e6dfb2deSDan Williams dev->groups = groups; 499f2989396SDave Jiang nvdimm->sec.ops = sec_ops; 5007d988097SDave Jiang nvdimm->sec.overwrite_tmo = 0; 5017d988097SDave Jiang INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query); 502f2989396SDave Jiang /* 503f2989396SDave Jiang * Security state must be initialized before device_add() for 504f2989396SDave Jiang * attribute visibility. 505f2989396SDave Jiang */ 50689fa9d8eSDave Jiang /* get security state and extended (master) state */ 507d78c620aSDan Williams nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 508d78c620aSDan Williams nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); 5094d88a97aSDan Williams nd_device_register(dev); 510e6dfb2deSDan Williams 511e6dfb2deSDan Williams return nvdimm; 512e6dfb2deSDan Williams } 513d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create); 5144d88a97aSDan Williams 5151cd73865SDan Williams static void shutdown_security_notify(void *data) 5167d988097SDave Jiang { 5171cd73865SDan Williams struct nvdimm *nvdimm = data; 5181cd73865SDan Williams 5191cd73865SDan Williams sysfs_put(nvdimm->sec.overwrite_state); 5201cd73865SDan Williams } 5211cd73865SDan Williams 5221cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev) 5231cd73865SDan Williams { 5241cd73865SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 5251cd73865SDan Williams 526d78c620aSDan Williams if (!nvdimm->sec.flags || !nvdimm->sec.ops 5271cd73865SDan Williams || !nvdimm->sec.ops->overwrite) 5287d988097SDave Jiang return 0; 5291cd73865SDan Williams nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); 5301cd73865SDan Williams if (!nvdimm->sec.overwrite_state) 5311cd73865SDan Williams return -ENOMEM; 5321cd73865SDan Williams 5331cd73865SDan Williams return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm); 5347d988097SDave Jiang } 5357d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events); 5367d988097SDave Jiang 5377d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm) 5387d988097SDave Jiang { 5397d988097SDave Jiang return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 5407d988097SDave Jiang } 5417d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite); 5427d988097SDave Jiang 54337833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm) 54437833fb7SDave Jiang { 54537833fb7SDave Jiang int rc; 54637833fb7SDave Jiang 54737833fb7SDave Jiang WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 54837833fb7SDave Jiang 54937833fb7SDave Jiang if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) 55037833fb7SDave Jiang return -EOPNOTSUPP; 55137833fb7SDave Jiang 552d78c620aSDan Williams if (!nvdimm->sec.flags) 55337833fb7SDave Jiang return -EIO; 55437833fb7SDave Jiang 5557d988097SDave Jiang if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 5567d988097SDave Jiang dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n"); 5577d988097SDave Jiang return -EBUSY; 5587d988097SDave Jiang } 5597d988097SDave Jiang 56037833fb7SDave Jiang rc = nvdimm->sec.ops->freeze(nvdimm); 561d78c620aSDan Williams nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 56237833fb7SDave Jiang 56337833fb7SDave Jiang return rc; 56437833fb7SDave Jiang } 56537833fb7SDave Jiang 5662522afb8SDan Williams static unsigned long dpa_align(struct nd_region *nd_region) 5672522afb8SDan Williams { 5682522afb8SDan Williams struct device *dev = &nd_region->dev; 5692522afb8SDan Williams 5702522afb8SDan Williams if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev), 5712522afb8SDan Williams "bus lock required for capacity provision\n")) 5722522afb8SDan Williams return 0; 5732522afb8SDan Williams if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align 5742522afb8SDan Williams % nd_region->ndr_mappings, 5752522afb8SDan Williams "invalid region align %#lx mappings: %d\n", 5762522afb8SDan Williams nd_region->align, nd_region->ndr_mappings)) 5772522afb8SDan Williams return 0; 5782522afb8SDan Williams return nd_region->align / nd_region->ndr_mappings; 5792522afb8SDan Williams } 5802522afb8SDan Williams 581762d067dSDan Williams int alias_dpa_busy(struct device *dev, void *data) 582a1f3e4d6SDan Williams { 583fe514739SDan Williams resource_size_t map_end, blk_start, new; 584a1f3e4d6SDan Williams struct blk_alloc_info *info = data; 585a1f3e4d6SDan Williams struct nd_mapping *nd_mapping; 586a1f3e4d6SDan Williams struct nd_region *nd_region; 587a1f3e4d6SDan Williams struct nvdimm_drvdata *ndd; 588a1f3e4d6SDan Williams struct resource *res; 5892522afb8SDan Williams unsigned long align; 590a1f3e4d6SDan Williams int i; 591a1f3e4d6SDan Williams 592c9e582aaSDan Williams if (!is_memory(dev)) 593a1f3e4d6SDan Williams return 0; 594a1f3e4d6SDan Williams 595a1f3e4d6SDan Williams nd_region = to_nd_region(dev); 596a1f3e4d6SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 597a1f3e4d6SDan Williams nd_mapping = &nd_region->mapping[i]; 598a1f3e4d6SDan Williams if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) 599a1f3e4d6SDan Williams break; 600a1f3e4d6SDan Williams } 601a1f3e4d6SDan Williams 602a1f3e4d6SDan Williams if (i >= nd_region->ndr_mappings) 603a1f3e4d6SDan Williams return 0; 604a1f3e4d6SDan Williams 605a1f3e4d6SDan Williams ndd = to_ndd(nd_mapping); 606a1f3e4d6SDan Williams map_end = nd_mapping->start + nd_mapping->size - 1; 607a1f3e4d6SDan Williams blk_start = nd_mapping->start; 608762d067dSDan Williams 609762d067dSDan Williams /* 610762d067dSDan Williams * In the allocation case ->res is set to free space that we are 611762d067dSDan Williams * looking to validate against PMEM aliasing collision rules 612762d067dSDan Williams * (i.e. BLK is allocated after all aliased PMEM). 613762d067dSDan Williams */ 614762d067dSDan Williams if (info->res) { 615762d067dSDan Williams if (info->res->start >= nd_mapping->start 616762d067dSDan Williams && info->res->start < map_end) 617762d067dSDan Williams /* pass */; 618762d067dSDan Williams else 619762d067dSDan Williams return 0; 620762d067dSDan Williams } 621762d067dSDan Williams 622a1f3e4d6SDan Williams retry: 623a1f3e4d6SDan Williams /* 624a1f3e4d6SDan Williams * Find the free dpa from the end of the last pmem allocation to 625fe514739SDan Williams * the end of the interleave-set mapping. 626a1f3e4d6SDan Williams */ 6272522afb8SDan Williams align = dpa_align(nd_region); 6282522afb8SDan Williams if (!align) 6292522afb8SDan Williams return 0; 6302522afb8SDan Williams 631a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 6322522afb8SDan Williams resource_size_t start, end; 6332522afb8SDan Williams 634fe514739SDan Williams if (strncmp(res->name, "pmem", 4) != 0) 635fe514739SDan Williams continue; 6362522afb8SDan Williams 6372522afb8SDan Williams start = ALIGN_DOWN(res->start, align); 6382522afb8SDan Williams end = ALIGN(res->end + 1, align) - 1; 6392522afb8SDan Williams if ((start >= blk_start && start < map_end) 6402522afb8SDan Williams || (end >= blk_start && end <= map_end)) { 6412522afb8SDan Williams new = max(blk_start, min(map_end, end) + 1); 642a1f3e4d6SDan Williams if (new != blk_start) { 643a1f3e4d6SDan Williams blk_start = new; 644a1f3e4d6SDan Williams goto retry; 645a1f3e4d6SDan Williams } 646a1f3e4d6SDan Williams } 647a1f3e4d6SDan Williams } 648a1f3e4d6SDan Williams 649762d067dSDan Williams /* update the free space range with the probed blk_start */ 650762d067dSDan Williams if (info->res && blk_start > info->res->start) { 651762d067dSDan Williams info->res->start = max(info->res->start, blk_start); 652762d067dSDan Williams if (info->res->start > info->res->end) 653762d067dSDan Williams info->res->end = info->res->start - 1; 654762d067dSDan Williams return 1; 655762d067dSDan Williams } 656762d067dSDan Williams 657fe514739SDan Williams info->available -= blk_start - nd_mapping->start; 658762d067dSDan Williams 659a1f3e4d6SDan Williams return 0; 660a1f3e4d6SDan Williams } 661a1f3e4d6SDan Williams 662bf9bccc1SDan Williams /** 6631b40e09aSDan Williams * nd_blk_available_dpa - account the unused dpa of BLK region 6641b40e09aSDan Williams * @nd_mapping: container of dpa-resource-root + labels 6651b40e09aSDan Williams * 666a1f3e4d6SDan Williams * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but 667a1f3e4d6SDan Williams * we arrange for them to never start at an lower dpa than the last 668a1f3e4d6SDan Williams * PMEM allocation in an aliased region. 6691b40e09aSDan Williams */ 670a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) 6711b40e09aSDan Williams { 672a1f3e4d6SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 673a1f3e4d6SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 6741b40e09aSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 675a1f3e4d6SDan Williams struct blk_alloc_info info = { 676a1f3e4d6SDan Williams .nd_mapping = nd_mapping, 677a1f3e4d6SDan Williams .available = nd_mapping->size, 678762d067dSDan Williams .res = NULL, 679a1f3e4d6SDan Williams }; 6801b40e09aSDan Williams struct resource *res; 6812522afb8SDan Williams unsigned long align; 6821b40e09aSDan Williams 6831b40e09aSDan Williams if (!ndd) 6841b40e09aSDan Williams return 0; 6851b40e09aSDan Williams 686a1f3e4d6SDan Williams device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); 6871b40e09aSDan Williams 688a1f3e4d6SDan Williams /* now account for busy blk allocations in unaliased dpa */ 6892522afb8SDan Williams align = dpa_align(nd_region); 6902522afb8SDan Williams if (!align) 6912522afb8SDan Williams return 0; 692a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 6932522afb8SDan Williams resource_size_t start, end, size; 6942522afb8SDan Williams 695a1f3e4d6SDan Williams if (strncmp(res->name, "blk", 3) != 0) 696a1f3e4d6SDan Williams continue; 6972522afb8SDan Williams start = ALIGN_DOWN(res->start, align); 6982522afb8SDan Williams end = ALIGN(res->end + 1, align) - 1; 6992522afb8SDan Williams size = end - start + 1; 7002522afb8SDan Williams if (size >= info.available) 7012522afb8SDan Williams return 0; 7022522afb8SDan Williams info.available -= size; 7031b40e09aSDan Williams } 7041b40e09aSDan Williams 705a1f3e4d6SDan Williams return info.available; 7061b40e09aSDan Williams } 7071b40e09aSDan Williams 7081b40e09aSDan Williams /** 70912e3129eSKeith Busch * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max 71012e3129eSKeith Busch * contiguous unallocated dpa range. 71112e3129eSKeith Busch * @nd_region: constrain available space check to this reference region 71212e3129eSKeith Busch * @nd_mapping: container of dpa-resource-root + labels 71312e3129eSKeith Busch */ 71412e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region, 71512e3129eSKeith Busch struct nd_mapping *nd_mapping) 71612e3129eSKeith Busch { 71712e3129eSKeith Busch struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 71812e3129eSKeith Busch struct nvdimm_bus *nvdimm_bus; 71912e3129eSKeith Busch resource_size_t max = 0; 72012e3129eSKeith Busch struct resource *res; 7212522afb8SDan Williams unsigned long align; 72212e3129eSKeith Busch 72312e3129eSKeith Busch /* if a dimm is disabled the available capacity is zero */ 72412e3129eSKeith Busch if (!ndd) 72512e3129eSKeith Busch return 0; 72612e3129eSKeith Busch 7272522afb8SDan Williams align = dpa_align(nd_region); 7282522afb8SDan Williams if (!align) 7292522afb8SDan Williams return 0; 7302522afb8SDan Williams 73112e3129eSKeith Busch nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 73212e3129eSKeith Busch if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm)) 73312e3129eSKeith Busch return 0; 73412e3129eSKeith Busch for_each_dpa_resource(ndd, res) { 7352522afb8SDan Williams resource_size_t start, end; 7362522afb8SDan Williams 73712e3129eSKeith Busch if (strcmp(res->name, "pmem-reserve") != 0) 73812e3129eSKeith Busch continue; 7392522afb8SDan Williams /* trim free space relative to current alignment setting */ 7402522afb8SDan Williams start = ALIGN(res->start, align); 7412522afb8SDan Williams end = ALIGN_DOWN(res->end + 1, align) - 1; 7422522afb8SDan Williams if (end < start) 7432522afb8SDan Williams continue; 7442522afb8SDan Williams if (end - start + 1 > max) 7452522afb8SDan Williams max = end - start + 1; 74612e3129eSKeith Busch } 74712e3129eSKeith Busch release_free_pmem(nvdimm_bus, nd_mapping); 74812e3129eSKeith Busch return max; 74912e3129eSKeith Busch } 75012e3129eSKeith Busch 75112e3129eSKeith Busch /** 752bf9bccc1SDan Williams * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 753bf9bccc1SDan Williams * @nd_mapping: container of dpa-resource-root + labels 754bf9bccc1SDan Williams * @nd_region: constrain available space check to this reference region 755bf9bccc1SDan Williams * @overlap: calculate available space assuming this level of overlap 756bf9bccc1SDan Williams * 757bf9bccc1SDan Williams * Validate that a PMEM label, if present, aligns with the start of an 758bf9bccc1SDan Williams * interleave set and truncate the available size at the lowest BLK 759bf9bccc1SDan Williams * overlap point. 760bf9bccc1SDan Williams * 761bf9bccc1SDan Williams * The expectation is that this routine is called multiple times as it 762bf9bccc1SDan Williams * probes for the largest BLK encroachment for any single member DIMM of 763bf9bccc1SDan Williams * the interleave set. Once that value is determined the PMEM-limit for 764bf9bccc1SDan Williams * the set can be established. 765bf9bccc1SDan Williams */ 766bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 767bf9bccc1SDan Williams struct nd_mapping *nd_mapping, resource_size_t *overlap) 768bf9bccc1SDan Williams { 769bf9bccc1SDan Williams resource_size_t map_start, map_end, busy = 0, available, blk_start; 770bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 771bf9bccc1SDan Williams struct resource *res; 772bf9bccc1SDan Williams const char *reason; 7732522afb8SDan Williams unsigned long align; 774bf9bccc1SDan Williams 775bf9bccc1SDan Williams if (!ndd) 776bf9bccc1SDan Williams return 0; 777bf9bccc1SDan Williams 7782522afb8SDan Williams align = dpa_align(nd_region); 7792522afb8SDan Williams if (!align) 7802522afb8SDan Williams return 0; 7812522afb8SDan Williams 782bf9bccc1SDan Williams map_start = nd_mapping->start; 783bf9bccc1SDan Williams map_end = map_start + nd_mapping->size - 1; 784bf9bccc1SDan Williams blk_start = max(map_start, map_end + 1 - *overlap); 785a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 7862522afb8SDan Williams resource_size_t start, end; 7872522afb8SDan Williams 7882522afb8SDan Williams start = ALIGN_DOWN(res->start, align); 7892522afb8SDan Williams end = ALIGN(res->end + 1, align) - 1; 7902522afb8SDan Williams if (start >= map_start && start < map_end) { 791bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) 792a1f3e4d6SDan Williams blk_start = min(blk_start, 7932522afb8SDan Williams max(map_start, start)); 7942522afb8SDan Williams else if (end > map_end) { 795bf9bccc1SDan Williams reason = "misaligned to iset"; 796bf9bccc1SDan Williams goto err; 797a1f3e4d6SDan Williams } else 7982522afb8SDan Williams busy += end - start + 1; 7992522afb8SDan Williams } else if (end >= map_start && end <= map_end) { 800bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) { 801bf9bccc1SDan Williams /* 802bf9bccc1SDan Williams * If a BLK allocation overlaps the start of 803bf9bccc1SDan Williams * PMEM the entire interleave set may now only 804bf9bccc1SDan Williams * be used for BLK. 805bf9bccc1SDan Williams */ 806bf9bccc1SDan Williams blk_start = map_start; 807a1f3e4d6SDan Williams } else 8082522afb8SDan Williams busy += end - start + 1; 8092522afb8SDan Williams } else if (map_start > start && map_start < end) { 810bf9bccc1SDan Williams /* total eclipse of the mapping */ 811bf9bccc1SDan Williams busy += nd_mapping->size; 812bf9bccc1SDan Williams blk_start = map_start; 813bf9bccc1SDan Williams } 814a1f3e4d6SDan Williams } 815bf9bccc1SDan Williams 816bf9bccc1SDan Williams *overlap = map_end + 1 - blk_start; 817bf9bccc1SDan Williams available = blk_start - map_start; 818bf9bccc1SDan Williams if (busy < available) 8192522afb8SDan Williams return ALIGN_DOWN(available - busy, align); 820bf9bccc1SDan Williams return 0; 821bf9bccc1SDan Williams 822bf9bccc1SDan Williams err: 823bf9bccc1SDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); 824bf9bccc1SDan Williams return 0; 825bf9bccc1SDan Williams } 826bf9bccc1SDan Williams 8274a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 8284a826c83SDan Williams { 8294a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8304a826c83SDan Williams kfree(res->name); 8314a826c83SDan Williams __release_region(&ndd->dpa, res->start, resource_size(res)); 8324a826c83SDan Williams } 8334a826c83SDan Williams 8344a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 8354a826c83SDan Williams struct nd_label_id *label_id, resource_size_t start, 8364a826c83SDan Williams resource_size_t n) 8374a826c83SDan Williams { 8384a826c83SDan Williams char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 8394a826c83SDan Williams struct resource *res; 8404a826c83SDan Williams 8414a826c83SDan Williams if (!name) 8424a826c83SDan Williams return NULL; 8434a826c83SDan Williams 8444a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 8454a826c83SDan Williams res = __request_region(&ndd->dpa, start, n, name, 0); 8464a826c83SDan Williams if (!res) 8474a826c83SDan Williams kfree(name); 8484a826c83SDan Williams return res; 8494a826c83SDan Williams } 8504a826c83SDan Williams 851bf9bccc1SDan Williams /** 852bf9bccc1SDan Williams * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 853bf9bccc1SDan Williams * @nvdimm: container of dpa-resource-root + labels 854bf9bccc1SDan Williams * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid> 855bf9bccc1SDan Williams */ 856bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 857bf9bccc1SDan Williams struct nd_label_id *label_id) 858bf9bccc1SDan Williams { 859bf9bccc1SDan Williams resource_size_t allocated = 0; 860bf9bccc1SDan Williams struct resource *res; 861bf9bccc1SDan Williams 862bf9bccc1SDan Williams for_each_dpa_resource(ndd, res) 863bf9bccc1SDan Williams if (strcmp(res->name, label_id->id) == 0) 864bf9bccc1SDan Williams allocated += resource_size(res); 865bf9bccc1SDan Williams 866bf9bccc1SDan Williams return allocated; 867bf9bccc1SDan Williams } 868bf9bccc1SDan Williams 8694d88a97aSDan Williams static int count_dimms(struct device *dev, void *c) 8704d88a97aSDan Williams { 8714d88a97aSDan Williams int *count = c; 8724d88a97aSDan Williams 8734d88a97aSDan Williams if (is_nvdimm(dev)) 8744d88a97aSDan Williams (*count)++; 8754d88a97aSDan Williams return 0; 8764d88a97aSDan Williams } 8774d88a97aSDan Williams 8784d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 8794d88a97aSDan Williams { 8804d88a97aSDan Williams int count = 0; 8814d88a97aSDan Williams /* Flush any possible dimm registration failures */ 8824d88a97aSDan Williams nd_synchronize(); 8834d88a97aSDan Williams 8844d88a97aSDan Williams device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 885426824d6SDan Williams dev_dbg(&nvdimm_bus->dev, "count: %d\n", count); 8864d88a97aSDan Williams if (count != dimm_count) 8874d88a97aSDan Williams return -ENXIO; 8884d88a97aSDan Williams return 0; 8894d88a97aSDan Williams } 8904d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 891b354aba0SDan Williams 892b354aba0SDan Williams void __exit nvdimm_devs_exit(void) 893b354aba0SDan Williams { 894b354aba0SDan Williams ida_destroy(&dimm_ida); 895b354aba0SDan Williams } 896