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 18742237e39SDan Williams void nvdimm_set_aliasing(struct device *dev) 18842237e39SDan Williams { 18942237e39SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 19042237e39SDan Williams 19142237e39SDan Williams nvdimm->flags |= NDD_ALIASING; 19242237e39SDan Williams } 19342237e39SDan Williams 194e6dfb2deSDan Williams static void nvdimm_release(struct device *dev) 195e6dfb2deSDan Williams { 196e6dfb2deSDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 197e6dfb2deSDan Williams 198e6dfb2deSDan Williams ida_simple_remove(&dimm_ida, nvdimm->id); 199e6dfb2deSDan Williams kfree(nvdimm); 200e6dfb2deSDan Williams } 201e6dfb2deSDan Williams 202e6dfb2deSDan Williams static struct device_type nvdimm_device_type = { 203e6dfb2deSDan Williams .name = "nvdimm", 204e6dfb2deSDan Williams .release = nvdimm_release, 205e6dfb2deSDan Williams }; 206e6dfb2deSDan Williams 20762232e45SDan Williams bool is_nvdimm(struct device *dev) 208e6dfb2deSDan Williams { 209e6dfb2deSDan Williams return dev->type == &nvdimm_device_type; 210e6dfb2deSDan Williams } 211e6dfb2deSDan Williams 212e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev) 213e6dfb2deSDan Williams { 214e6dfb2deSDan Williams struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); 215e6dfb2deSDan Williams 216e6dfb2deSDan Williams WARN_ON(!is_nvdimm(dev)); 217e6dfb2deSDan Williams return nvdimm; 218e6dfb2deSDan Williams } 219e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm); 220e6dfb2deSDan Williams 221047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr) 222047fc8a1SRoss Zwisler { 223047fc8a1SRoss Zwisler struct nd_region *nd_region = &ndbr->nd_region; 224047fc8a1SRoss Zwisler struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 225047fc8a1SRoss Zwisler 226047fc8a1SRoss Zwisler return nd_mapping->nvdimm; 227047fc8a1SRoss Zwisler } 228047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm); 229047fc8a1SRoss Zwisler 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 246bf9bccc1SDan Williams dev_dbg(dev, "%s\n", __func__); 247bf9bccc1SDan Williams 248bf9bccc1SDan Williams nvdimm_bus_lock(dev); 249bf9bccc1SDan Williams for_each_dpa_resource_safe(ndd, res, _r) 250bf9bccc1SDan Williams nvdimm_free_dpa(ndd, res); 251bf9bccc1SDan Williams nvdimm_bus_unlock(dev); 252bf9bccc1SDan Williams 253a06a7576Syalin wang kvfree(ndd->data); 254bf9bccc1SDan Williams kfree(ndd); 255bf9bccc1SDan Williams put_device(dev); 256bf9bccc1SDan Williams } 257bf9bccc1SDan Williams 258bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd) 259bf9bccc1SDan Williams { 260bf9bccc1SDan Williams kref_get(&ndd->kref); 261bf9bccc1SDan Williams } 262bf9bccc1SDan Williams 263bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd) 264bf9bccc1SDan Williams { 265bf9bccc1SDan Williams if (ndd) 266bf9bccc1SDan Williams kref_put(&ndd->kref, nvdimm_drvdata_release); 267bf9bccc1SDan Williams } 268bf9bccc1SDan Williams 269e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm) 270e6dfb2deSDan Williams { 271e6dfb2deSDan Williams return dev_name(&nvdimm->dev); 272e6dfb2deSDan Williams } 273e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name); 274e6dfb2deSDan Williams 275ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) 276ba9c8dd3SDan Williams { 277ba9c8dd3SDan Williams return &nvdimm->dev.kobj; 278ba9c8dd3SDan Williams } 279ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj); 280ba9c8dd3SDan Williams 281e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) 282e3654ecaSDan Williams { 283e3654ecaSDan Williams return nvdimm->cmd_mask; 284e3654ecaSDan Williams } 285e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask); 286e3654ecaSDan Williams 287e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm) 288e6dfb2deSDan Williams { 28962232e45SDan Williams if (nvdimm) 290e6dfb2deSDan Williams return nvdimm->provider_data; 29162232e45SDan Williams return NULL; 292e6dfb2deSDan Williams } 293e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data); 294e6dfb2deSDan Williams 29562232e45SDan Williams static ssize_t commands_show(struct device *dev, 29662232e45SDan Williams struct device_attribute *attr, char *buf) 29762232e45SDan Williams { 29862232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 29962232e45SDan Williams int cmd, len = 0; 30062232e45SDan Williams 301e3654ecaSDan Williams if (!nvdimm->cmd_mask) 30262232e45SDan Williams return sprintf(buf, "\n"); 30362232e45SDan Williams 304e3654ecaSDan Williams for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) 30562232e45SDan Williams len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); 30662232e45SDan Williams len += sprintf(buf + len, "\n"); 30762232e45SDan Williams return len; 30862232e45SDan Williams } 30962232e45SDan Williams static DEVICE_ATTR_RO(commands); 31062232e45SDan Williams 311eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr, 312eaf96153SDan Williams char *buf) 313eaf96153SDan Williams { 314eaf96153SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 315eaf96153SDan Williams 316eaf96153SDan Williams /* 317eaf96153SDan Williams * The state may be in the process of changing, userspace should 318eaf96153SDan Williams * quiesce probing if it wants a static answer 319eaf96153SDan Williams */ 320eaf96153SDan Williams nvdimm_bus_lock(dev); 321eaf96153SDan Williams nvdimm_bus_unlock(dev); 322eaf96153SDan Williams return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) 323eaf96153SDan Williams ? "active" : "idle"); 324eaf96153SDan Williams } 325eaf96153SDan Williams static DEVICE_ATTR_RO(state); 326eaf96153SDan Williams 3270ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev, 3280ba1c634SDan Williams struct device_attribute *attr, char *buf) 3290ba1c634SDan Williams { 3300ba1c634SDan Williams struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); 3310ba1c634SDan Williams ssize_t rc; 3320ba1c634SDan Williams u32 nfree; 3330ba1c634SDan Williams 3340ba1c634SDan Williams if (!ndd) 3350ba1c634SDan Williams return -ENXIO; 3360ba1c634SDan Williams 3370ba1c634SDan Williams nvdimm_bus_lock(dev); 3380ba1c634SDan Williams nfree = nd_label_nfree(ndd); 3390ba1c634SDan Williams if (nfree - 1 > nfree) { 3400ba1c634SDan Williams dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); 3410ba1c634SDan Williams nfree = 0; 3420ba1c634SDan Williams } else 3430ba1c634SDan Williams nfree--; 3440ba1c634SDan Williams rc = sprintf(buf, "%d\n", nfree); 3450ba1c634SDan Williams nvdimm_bus_unlock(dev); 3460ba1c634SDan Williams return rc; 3470ba1c634SDan Williams } 3480ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots); 3490ba1c634SDan Williams 35062232e45SDan Williams static struct attribute *nvdimm_attributes[] = { 351eaf96153SDan Williams &dev_attr_state.attr, 35262232e45SDan Williams &dev_attr_commands.attr, 3530ba1c634SDan Williams &dev_attr_available_slots.attr, 35462232e45SDan Williams NULL, 35562232e45SDan Williams }; 35662232e45SDan Williams 35762232e45SDan Williams struct attribute_group nvdimm_attribute_group = { 35862232e45SDan Williams .attrs = nvdimm_attributes, 35962232e45SDan Williams }; 36062232e45SDan Williams EXPORT_SYMBOL_GPL(nvdimm_attribute_group); 36162232e45SDan Williams 362e6dfb2deSDan Williams struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, 36362232e45SDan Williams const struct attribute_group **groups, unsigned long flags, 364e5ae3b25SDan Williams unsigned long cmd_mask, int num_flush, 365e5ae3b25SDan Williams struct resource *flush_wpq) 366e6dfb2deSDan Williams { 367e6dfb2deSDan Williams struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 368e6dfb2deSDan Williams struct device *dev; 369e6dfb2deSDan Williams 370e6dfb2deSDan Williams if (!nvdimm) 371e6dfb2deSDan Williams return NULL; 372e6dfb2deSDan Williams 373e6dfb2deSDan Williams nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); 374e6dfb2deSDan Williams if (nvdimm->id < 0) { 375e6dfb2deSDan Williams kfree(nvdimm); 376e6dfb2deSDan Williams return NULL; 377e6dfb2deSDan Williams } 378e6dfb2deSDan Williams nvdimm->provider_data = provider_data; 379e6dfb2deSDan Williams nvdimm->flags = flags; 380e3654ecaSDan Williams nvdimm->cmd_mask = cmd_mask; 381e5ae3b25SDan Williams nvdimm->num_flush = num_flush; 382e5ae3b25SDan Williams nvdimm->flush_wpq = flush_wpq; 383eaf96153SDan Williams atomic_set(&nvdimm->busy, 0); 384e6dfb2deSDan Williams dev = &nvdimm->dev; 385e6dfb2deSDan Williams dev_set_name(dev, "nmem%d", nvdimm->id); 386e6dfb2deSDan Williams dev->parent = &nvdimm_bus->dev; 387e6dfb2deSDan Williams dev->type = &nvdimm_device_type; 38862232e45SDan Williams dev->devt = MKDEV(nvdimm_major, nvdimm->id); 389e6dfb2deSDan Williams dev->groups = groups; 3904d88a97aSDan Williams nd_device_register(dev); 391e6dfb2deSDan Williams 392e6dfb2deSDan Williams return nvdimm; 393e6dfb2deSDan Williams } 394e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_create); 3954d88a97aSDan Williams 396762d067dSDan Williams int alias_dpa_busy(struct device *dev, void *data) 397a1f3e4d6SDan Williams { 398a1f3e4d6SDan Williams resource_size_t map_end, blk_start, new, busy; 399a1f3e4d6SDan Williams struct blk_alloc_info *info = data; 400a1f3e4d6SDan Williams struct nd_mapping *nd_mapping; 401a1f3e4d6SDan Williams struct nd_region *nd_region; 402a1f3e4d6SDan Williams struct nvdimm_drvdata *ndd; 403a1f3e4d6SDan Williams struct resource *res; 404a1f3e4d6SDan Williams int i; 405a1f3e4d6SDan Williams 406a1f3e4d6SDan Williams if (!is_nd_pmem(dev)) 407a1f3e4d6SDan Williams return 0; 408a1f3e4d6SDan Williams 409a1f3e4d6SDan Williams nd_region = to_nd_region(dev); 410a1f3e4d6SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 411a1f3e4d6SDan Williams nd_mapping = &nd_region->mapping[i]; 412a1f3e4d6SDan Williams if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) 413a1f3e4d6SDan Williams break; 414a1f3e4d6SDan Williams } 415a1f3e4d6SDan Williams 416a1f3e4d6SDan Williams if (i >= nd_region->ndr_mappings) 417a1f3e4d6SDan Williams return 0; 418a1f3e4d6SDan Williams 419a1f3e4d6SDan Williams ndd = to_ndd(nd_mapping); 420a1f3e4d6SDan Williams map_end = nd_mapping->start + nd_mapping->size - 1; 421a1f3e4d6SDan Williams blk_start = nd_mapping->start; 422762d067dSDan Williams 423762d067dSDan Williams /* 424762d067dSDan Williams * In the allocation case ->res is set to free space that we are 425762d067dSDan Williams * looking to validate against PMEM aliasing collision rules 426762d067dSDan Williams * (i.e. BLK is allocated after all aliased PMEM). 427762d067dSDan Williams */ 428762d067dSDan Williams if (info->res) { 429762d067dSDan Williams if (info->res->start >= nd_mapping->start 430762d067dSDan Williams && info->res->start < map_end) 431762d067dSDan Williams /* pass */; 432762d067dSDan Williams else 433762d067dSDan Williams return 0; 434762d067dSDan Williams } 435762d067dSDan Williams 436a1f3e4d6SDan Williams retry: 437a1f3e4d6SDan Williams /* 438a1f3e4d6SDan Williams * Find the free dpa from the end of the last pmem allocation to 439a1f3e4d6SDan Williams * the end of the interleave-set mapping that is not already 440a1f3e4d6SDan Williams * covered by a blk allocation. 441a1f3e4d6SDan Williams */ 442a1f3e4d6SDan Williams busy = 0; 443a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 444a1f3e4d6SDan Williams if ((res->start >= blk_start && res->start < map_end) 445a1f3e4d6SDan Williams || (res->end >= blk_start 446a1f3e4d6SDan Williams && res->end <= map_end)) { 447a1f3e4d6SDan Williams if (strncmp(res->name, "pmem", 4) == 0) { 448a1f3e4d6SDan Williams new = max(blk_start, min(map_end + 1, 449a1f3e4d6SDan Williams res->end + 1)); 450a1f3e4d6SDan Williams if (new != blk_start) { 451a1f3e4d6SDan Williams blk_start = new; 452a1f3e4d6SDan Williams goto retry; 453a1f3e4d6SDan Williams } 454a1f3e4d6SDan Williams } else 455a1f3e4d6SDan Williams busy += min(map_end, res->end) 456a1f3e4d6SDan Williams - max(nd_mapping->start, res->start) + 1; 457a1f3e4d6SDan Williams } else if (nd_mapping->start > res->start 458a1f3e4d6SDan Williams && map_end < res->end) { 459a1f3e4d6SDan Williams /* total eclipse of the PMEM region mapping */ 460a1f3e4d6SDan Williams busy += nd_mapping->size; 461a1f3e4d6SDan Williams break; 462a1f3e4d6SDan Williams } 463a1f3e4d6SDan Williams } 464a1f3e4d6SDan Williams 465762d067dSDan Williams /* update the free space range with the probed blk_start */ 466762d067dSDan Williams if (info->res && blk_start > info->res->start) { 467762d067dSDan Williams info->res->start = max(info->res->start, blk_start); 468762d067dSDan Williams if (info->res->start > info->res->end) 469762d067dSDan Williams info->res->end = info->res->start - 1; 470762d067dSDan Williams return 1; 471762d067dSDan Williams } 472762d067dSDan Williams 473a1f3e4d6SDan Williams info->available -= blk_start - nd_mapping->start + busy; 474762d067dSDan Williams 475a1f3e4d6SDan Williams return 0; 476a1f3e4d6SDan Williams } 477a1f3e4d6SDan Williams 478a1f3e4d6SDan Williams static int blk_dpa_busy(struct device *dev, void *data) 479a1f3e4d6SDan Williams { 480a1f3e4d6SDan Williams struct blk_alloc_info *info = data; 481a1f3e4d6SDan Williams struct nd_mapping *nd_mapping; 482a1f3e4d6SDan Williams struct nd_region *nd_region; 483a1f3e4d6SDan Williams resource_size_t map_end; 484a1f3e4d6SDan Williams int i; 485a1f3e4d6SDan Williams 486a1f3e4d6SDan Williams if (!is_nd_pmem(dev)) 487a1f3e4d6SDan Williams return 0; 488a1f3e4d6SDan Williams 489a1f3e4d6SDan Williams nd_region = to_nd_region(dev); 490a1f3e4d6SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 491a1f3e4d6SDan Williams nd_mapping = &nd_region->mapping[i]; 492a1f3e4d6SDan Williams if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) 493a1f3e4d6SDan Williams break; 494a1f3e4d6SDan Williams } 495a1f3e4d6SDan Williams 496a1f3e4d6SDan Williams if (i >= nd_region->ndr_mappings) 497a1f3e4d6SDan Williams return 0; 498a1f3e4d6SDan Williams 499a1f3e4d6SDan Williams map_end = nd_mapping->start + nd_mapping->size - 1; 500a1f3e4d6SDan Williams if (info->res->start >= nd_mapping->start 501a1f3e4d6SDan Williams && info->res->start < map_end) { 502a1f3e4d6SDan Williams if (info->res->end <= map_end) { 503a1f3e4d6SDan Williams info->busy = 0; 504a1f3e4d6SDan Williams return 1; 505a1f3e4d6SDan Williams } else { 506a1f3e4d6SDan Williams info->busy -= info->res->end - map_end; 507a1f3e4d6SDan Williams return 0; 508a1f3e4d6SDan Williams } 509a1f3e4d6SDan Williams } else if (info->res->end >= nd_mapping->start 510a1f3e4d6SDan Williams && info->res->end <= map_end) { 511a1f3e4d6SDan Williams info->busy -= nd_mapping->start - info->res->start; 512a1f3e4d6SDan Williams return 0; 513a1f3e4d6SDan Williams } else { 514a1f3e4d6SDan Williams info->busy -= nd_mapping->size; 515a1f3e4d6SDan Williams return 0; 516a1f3e4d6SDan Williams } 517a1f3e4d6SDan Williams } 518a1f3e4d6SDan Williams 519bf9bccc1SDan Williams /** 5201b40e09aSDan Williams * nd_blk_available_dpa - account the unused dpa of BLK region 5211b40e09aSDan Williams * @nd_mapping: container of dpa-resource-root + labels 5221b40e09aSDan Williams * 523a1f3e4d6SDan Williams * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but 524a1f3e4d6SDan Williams * we arrange for them to never start at an lower dpa than the last 525a1f3e4d6SDan Williams * PMEM allocation in an aliased region. 5261b40e09aSDan Williams */ 527a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) 5281b40e09aSDan Williams { 529a1f3e4d6SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 530a1f3e4d6SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 5311b40e09aSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 532a1f3e4d6SDan Williams struct blk_alloc_info info = { 533a1f3e4d6SDan Williams .nd_mapping = nd_mapping, 534a1f3e4d6SDan Williams .available = nd_mapping->size, 535762d067dSDan Williams .res = NULL, 536a1f3e4d6SDan Williams }; 5371b40e09aSDan Williams struct resource *res; 5381b40e09aSDan Williams 5391b40e09aSDan Williams if (!ndd) 5401b40e09aSDan Williams return 0; 5411b40e09aSDan Williams 542a1f3e4d6SDan Williams device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); 5431b40e09aSDan Williams 544a1f3e4d6SDan Williams /* now account for busy blk allocations in unaliased dpa */ 545a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 546a1f3e4d6SDan Williams if (strncmp(res->name, "blk", 3) != 0) 547a1f3e4d6SDan Williams continue; 548a1f3e4d6SDan Williams 549a1f3e4d6SDan Williams info.res = res; 550a1f3e4d6SDan Williams info.busy = resource_size(res); 551a1f3e4d6SDan Williams device_for_each_child(&nvdimm_bus->dev, &info, blk_dpa_busy); 552a1f3e4d6SDan Williams info.available -= info.busy; 5531b40e09aSDan Williams } 5541b40e09aSDan Williams 555a1f3e4d6SDan Williams return info.available; 5561b40e09aSDan Williams } 5571b40e09aSDan Williams 5581b40e09aSDan Williams /** 559bf9bccc1SDan Williams * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 560bf9bccc1SDan Williams * @nd_mapping: container of dpa-resource-root + labels 561bf9bccc1SDan Williams * @nd_region: constrain available space check to this reference region 562bf9bccc1SDan Williams * @overlap: calculate available space assuming this level of overlap 563bf9bccc1SDan Williams * 564bf9bccc1SDan Williams * Validate that a PMEM label, if present, aligns with the start of an 565bf9bccc1SDan Williams * interleave set and truncate the available size at the lowest BLK 566bf9bccc1SDan Williams * overlap point. 567bf9bccc1SDan Williams * 568bf9bccc1SDan Williams * The expectation is that this routine is called multiple times as it 569bf9bccc1SDan Williams * probes for the largest BLK encroachment for any single member DIMM of 570bf9bccc1SDan Williams * the interleave set. Once that value is determined the PMEM-limit for 571bf9bccc1SDan Williams * the set can be established. 572bf9bccc1SDan Williams */ 573bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 574bf9bccc1SDan Williams struct nd_mapping *nd_mapping, resource_size_t *overlap) 575bf9bccc1SDan Williams { 576bf9bccc1SDan Williams resource_size_t map_start, map_end, busy = 0, available, blk_start; 577bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 578bf9bccc1SDan Williams struct resource *res; 579bf9bccc1SDan Williams const char *reason; 580bf9bccc1SDan Williams 581bf9bccc1SDan Williams if (!ndd) 582bf9bccc1SDan Williams return 0; 583bf9bccc1SDan Williams 584bf9bccc1SDan Williams map_start = nd_mapping->start; 585bf9bccc1SDan Williams map_end = map_start + nd_mapping->size - 1; 586bf9bccc1SDan Williams blk_start = max(map_start, map_end + 1 - *overlap); 587a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 588bf9bccc1SDan Williams if (res->start >= map_start && res->start < map_end) { 589bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) 590a1f3e4d6SDan Williams blk_start = min(blk_start, 591a1f3e4d6SDan Williams max(map_start, res->start)); 592a1f3e4d6SDan Williams else if (res->end > map_end) { 593bf9bccc1SDan Williams reason = "misaligned to iset"; 594bf9bccc1SDan Williams goto err; 595a1f3e4d6SDan Williams } else 596bf9bccc1SDan Williams busy += resource_size(res); 597bf9bccc1SDan Williams } else if (res->end >= map_start && res->end <= map_end) { 598bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) { 599bf9bccc1SDan Williams /* 600bf9bccc1SDan Williams * If a BLK allocation overlaps the start of 601bf9bccc1SDan Williams * PMEM the entire interleave set may now only 602bf9bccc1SDan Williams * be used for BLK. 603bf9bccc1SDan Williams */ 604bf9bccc1SDan Williams blk_start = map_start; 605a1f3e4d6SDan Williams } else 606a1f3e4d6SDan Williams busy += resource_size(res); 607bf9bccc1SDan Williams } else if (map_start > res->start && map_start < res->end) { 608bf9bccc1SDan Williams /* total eclipse of the mapping */ 609bf9bccc1SDan Williams busy += nd_mapping->size; 610bf9bccc1SDan Williams blk_start = map_start; 611bf9bccc1SDan Williams } 612a1f3e4d6SDan Williams } 613bf9bccc1SDan Williams 614bf9bccc1SDan Williams *overlap = map_end + 1 - blk_start; 615bf9bccc1SDan Williams available = blk_start - map_start; 616bf9bccc1SDan Williams if (busy < available) 617bf9bccc1SDan Williams return available - busy; 618bf9bccc1SDan Williams return 0; 619bf9bccc1SDan Williams 620bf9bccc1SDan Williams err: 621bf9bccc1SDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); 622bf9bccc1SDan Williams return 0; 623bf9bccc1SDan Williams } 624bf9bccc1SDan Williams 6254a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 6264a826c83SDan Williams { 6274a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 6284a826c83SDan Williams kfree(res->name); 6294a826c83SDan Williams __release_region(&ndd->dpa, res->start, resource_size(res)); 6304a826c83SDan Williams } 6314a826c83SDan Williams 6324a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 6334a826c83SDan Williams struct nd_label_id *label_id, resource_size_t start, 6344a826c83SDan Williams resource_size_t n) 6354a826c83SDan Williams { 6364a826c83SDan Williams char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 6374a826c83SDan Williams struct resource *res; 6384a826c83SDan Williams 6394a826c83SDan Williams if (!name) 6404a826c83SDan Williams return NULL; 6414a826c83SDan Williams 6424a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 6434a826c83SDan Williams res = __request_region(&ndd->dpa, start, n, name, 0); 6444a826c83SDan Williams if (!res) 6454a826c83SDan Williams kfree(name); 6464a826c83SDan Williams return res; 6474a826c83SDan Williams } 6484a826c83SDan Williams 649bf9bccc1SDan Williams /** 650bf9bccc1SDan Williams * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 651bf9bccc1SDan Williams * @nvdimm: container of dpa-resource-root + labels 652bf9bccc1SDan Williams * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid> 653bf9bccc1SDan Williams */ 654bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 655bf9bccc1SDan Williams struct nd_label_id *label_id) 656bf9bccc1SDan Williams { 657bf9bccc1SDan Williams resource_size_t allocated = 0; 658bf9bccc1SDan Williams struct resource *res; 659bf9bccc1SDan Williams 660bf9bccc1SDan Williams for_each_dpa_resource(ndd, res) 661bf9bccc1SDan Williams if (strcmp(res->name, label_id->id) == 0) 662bf9bccc1SDan Williams allocated += resource_size(res); 663bf9bccc1SDan Williams 664bf9bccc1SDan Williams return allocated; 665bf9bccc1SDan Williams } 666bf9bccc1SDan Williams 6674d88a97aSDan Williams static int count_dimms(struct device *dev, void *c) 6684d88a97aSDan Williams { 6694d88a97aSDan Williams int *count = c; 6704d88a97aSDan Williams 6714d88a97aSDan Williams if (is_nvdimm(dev)) 6724d88a97aSDan Williams (*count)++; 6734d88a97aSDan Williams return 0; 6744d88a97aSDan Williams } 6754d88a97aSDan Williams 6764d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 6774d88a97aSDan Williams { 6784d88a97aSDan Williams int count = 0; 6794d88a97aSDan Williams /* Flush any possible dimm registration failures */ 6804d88a97aSDan Williams nd_synchronize(); 6814d88a97aSDan Williams 6824d88a97aSDan Williams device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 6834d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count); 6844d88a97aSDan Williams if (count != dimm_count) 6854d88a97aSDan Williams return -ENXIO; 6864d88a97aSDan Williams return 0; 6874d88a97aSDan Williams } 6884d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 689b354aba0SDan Williams 690b354aba0SDan Williams void __exit nvdimm_devs_exit(void) 691b354aba0SDan Williams { 692b354aba0SDan Williams ida_destroy(&dimm_ida); 693b354aba0SDan Williams } 694