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