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 { 398fe514739SDan Williams resource_size_t map_end, blk_start, new; 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 439fe514739SDan Williams * the end of the interleave-set mapping. 440a1f3e4d6SDan Williams */ 441a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 442fe514739SDan Williams if (strncmp(res->name, "pmem", 4) != 0) 443fe514739SDan Williams continue; 444a1f3e4d6SDan Williams if ((res->start >= blk_start && res->start < map_end) 445a1f3e4d6SDan Williams || (res->end >= blk_start 446a1f3e4d6SDan Williams && res->end <= map_end)) { 447fe514739SDan Williams new = max(blk_start, min(map_end + 1, res->end + 1)); 448a1f3e4d6SDan Williams if (new != blk_start) { 449a1f3e4d6SDan Williams blk_start = new; 450a1f3e4d6SDan Williams goto retry; 451a1f3e4d6SDan Williams } 452a1f3e4d6SDan Williams } 453a1f3e4d6SDan Williams } 454a1f3e4d6SDan Williams 455762d067dSDan Williams /* update the free space range with the probed blk_start */ 456762d067dSDan Williams if (info->res && blk_start > info->res->start) { 457762d067dSDan Williams info->res->start = max(info->res->start, blk_start); 458762d067dSDan Williams if (info->res->start > info->res->end) 459762d067dSDan Williams info->res->end = info->res->start - 1; 460762d067dSDan Williams return 1; 461762d067dSDan Williams } 462762d067dSDan Williams 463fe514739SDan Williams info->available -= blk_start - nd_mapping->start; 464762d067dSDan Williams 465a1f3e4d6SDan Williams return 0; 466a1f3e4d6SDan Williams } 467a1f3e4d6SDan Williams 468bf9bccc1SDan Williams /** 4691b40e09aSDan Williams * nd_blk_available_dpa - account the unused dpa of BLK region 4701b40e09aSDan Williams * @nd_mapping: container of dpa-resource-root + labels 4711b40e09aSDan Williams * 472a1f3e4d6SDan Williams * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but 473a1f3e4d6SDan Williams * we arrange for them to never start at an lower dpa than the last 474a1f3e4d6SDan Williams * PMEM allocation in an aliased region. 4751b40e09aSDan Williams */ 476a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) 4771b40e09aSDan Williams { 478a1f3e4d6SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 479a1f3e4d6SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 4801b40e09aSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 481a1f3e4d6SDan Williams struct blk_alloc_info info = { 482a1f3e4d6SDan Williams .nd_mapping = nd_mapping, 483a1f3e4d6SDan Williams .available = nd_mapping->size, 484762d067dSDan Williams .res = NULL, 485a1f3e4d6SDan Williams }; 4861b40e09aSDan Williams struct resource *res; 4871b40e09aSDan Williams 4881b40e09aSDan Williams if (!ndd) 4891b40e09aSDan Williams return 0; 4901b40e09aSDan Williams 491a1f3e4d6SDan Williams device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); 4921b40e09aSDan Williams 493a1f3e4d6SDan Williams /* now account for busy blk allocations in unaliased dpa */ 494a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 495a1f3e4d6SDan Williams if (strncmp(res->name, "blk", 3) != 0) 496a1f3e4d6SDan Williams continue; 497fe514739SDan Williams info.available -= resource_size(res); 4981b40e09aSDan Williams } 4991b40e09aSDan Williams 500a1f3e4d6SDan Williams return info.available; 5011b40e09aSDan Williams } 5021b40e09aSDan Williams 5031b40e09aSDan Williams /** 504bf9bccc1SDan Williams * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 505bf9bccc1SDan Williams * @nd_mapping: container of dpa-resource-root + labels 506bf9bccc1SDan Williams * @nd_region: constrain available space check to this reference region 507bf9bccc1SDan Williams * @overlap: calculate available space assuming this level of overlap 508bf9bccc1SDan Williams * 509bf9bccc1SDan Williams * Validate that a PMEM label, if present, aligns with the start of an 510bf9bccc1SDan Williams * interleave set and truncate the available size at the lowest BLK 511bf9bccc1SDan Williams * overlap point. 512bf9bccc1SDan Williams * 513bf9bccc1SDan Williams * The expectation is that this routine is called multiple times as it 514bf9bccc1SDan Williams * probes for the largest BLK encroachment for any single member DIMM of 515bf9bccc1SDan Williams * the interleave set. Once that value is determined the PMEM-limit for 516bf9bccc1SDan Williams * the set can be established. 517bf9bccc1SDan Williams */ 518bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 519bf9bccc1SDan Williams struct nd_mapping *nd_mapping, resource_size_t *overlap) 520bf9bccc1SDan Williams { 521bf9bccc1SDan Williams resource_size_t map_start, map_end, busy = 0, available, blk_start; 522bf9bccc1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 523bf9bccc1SDan Williams struct resource *res; 524bf9bccc1SDan Williams const char *reason; 525bf9bccc1SDan Williams 526bf9bccc1SDan Williams if (!ndd) 527bf9bccc1SDan Williams return 0; 528bf9bccc1SDan Williams 529bf9bccc1SDan Williams map_start = nd_mapping->start; 530bf9bccc1SDan Williams map_end = map_start + nd_mapping->size - 1; 531bf9bccc1SDan Williams blk_start = max(map_start, map_end + 1 - *overlap); 532a1f3e4d6SDan Williams for_each_dpa_resource(ndd, res) { 533bf9bccc1SDan Williams if (res->start >= map_start && res->start < map_end) { 534bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) 535a1f3e4d6SDan Williams blk_start = min(blk_start, 536a1f3e4d6SDan Williams max(map_start, res->start)); 537a1f3e4d6SDan Williams else if (res->end > map_end) { 538bf9bccc1SDan Williams reason = "misaligned to iset"; 539bf9bccc1SDan Williams goto err; 540a1f3e4d6SDan Williams } else 541bf9bccc1SDan Williams busy += resource_size(res); 542bf9bccc1SDan Williams } else if (res->end >= map_start && res->end <= map_end) { 543bf9bccc1SDan Williams if (strncmp(res->name, "blk", 3) == 0) { 544bf9bccc1SDan Williams /* 545bf9bccc1SDan Williams * If a BLK allocation overlaps the start of 546bf9bccc1SDan Williams * PMEM the entire interleave set may now only 547bf9bccc1SDan Williams * be used for BLK. 548bf9bccc1SDan Williams */ 549bf9bccc1SDan Williams blk_start = map_start; 550a1f3e4d6SDan Williams } else 551a1f3e4d6SDan Williams busy += resource_size(res); 552bf9bccc1SDan Williams } else if (map_start > res->start && map_start < res->end) { 553bf9bccc1SDan Williams /* total eclipse of the mapping */ 554bf9bccc1SDan Williams busy += nd_mapping->size; 555bf9bccc1SDan Williams blk_start = map_start; 556bf9bccc1SDan Williams } 557a1f3e4d6SDan Williams } 558bf9bccc1SDan Williams 559bf9bccc1SDan Williams *overlap = map_end + 1 - blk_start; 560bf9bccc1SDan Williams available = blk_start - map_start; 561bf9bccc1SDan Williams if (busy < available) 562bf9bccc1SDan Williams return available - busy; 563bf9bccc1SDan Williams return 0; 564bf9bccc1SDan Williams 565bf9bccc1SDan Williams err: 566bf9bccc1SDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); 567bf9bccc1SDan Williams return 0; 568bf9bccc1SDan Williams } 569bf9bccc1SDan Williams 5704a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 5714a826c83SDan Williams { 5724a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 5734a826c83SDan Williams kfree(res->name); 5744a826c83SDan Williams __release_region(&ndd->dpa, res->start, resource_size(res)); 5754a826c83SDan Williams } 5764a826c83SDan Williams 5774a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 5784a826c83SDan Williams struct nd_label_id *label_id, resource_size_t start, 5794a826c83SDan Williams resource_size_t n) 5804a826c83SDan Williams { 5814a826c83SDan Williams char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 5824a826c83SDan Williams struct resource *res; 5834a826c83SDan Williams 5844a826c83SDan Williams if (!name) 5854a826c83SDan Williams return NULL; 5864a826c83SDan Williams 5874a826c83SDan Williams WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 5884a826c83SDan Williams res = __request_region(&ndd->dpa, start, n, name, 0); 5894a826c83SDan Williams if (!res) 5904a826c83SDan Williams kfree(name); 5914a826c83SDan Williams return res; 5924a826c83SDan Williams } 5934a826c83SDan Williams 594bf9bccc1SDan Williams /** 595bf9bccc1SDan Williams * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 596bf9bccc1SDan Williams * @nvdimm: container of dpa-resource-root + labels 597bf9bccc1SDan Williams * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid> 598bf9bccc1SDan Williams */ 599bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 600bf9bccc1SDan Williams struct nd_label_id *label_id) 601bf9bccc1SDan Williams { 602bf9bccc1SDan Williams resource_size_t allocated = 0; 603bf9bccc1SDan Williams struct resource *res; 604bf9bccc1SDan Williams 605bf9bccc1SDan Williams for_each_dpa_resource(ndd, res) 606bf9bccc1SDan Williams if (strcmp(res->name, label_id->id) == 0) 607bf9bccc1SDan Williams allocated += resource_size(res); 608bf9bccc1SDan Williams 609bf9bccc1SDan Williams return allocated; 610bf9bccc1SDan Williams } 611bf9bccc1SDan Williams 6124d88a97aSDan Williams static int count_dimms(struct device *dev, void *c) 6134d88a97aSDan Williams { 6144d88a97aSDan Williams int *count = c; 6154d88a97aSDan Williams 6164d88a97aSDan Williams if (is_nvdimm(dev)) 6174d88a97aSDan Williams (*count)++; 6184d88a97aSDan Williams return 0; 6194d88a97aSDan Williams } 6204d88a97aSDan Williams 6214d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 6224d88a97aSDan Williams { 6234d88a97aSDan Williams int count = 0; 6244d88a97aSDan Williams /* Flush any possible dimm registration failures */ 6254d88a97aSDan Williams nd_synchronize(); 6264d88a97aSDan Williams 6274d88a97aSDan Williams device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 6284d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s: count: %d\n", __func__, count); 6294d88a97aSDan Williams if (count != dimm_count) 6304d88a97aSDan Williams return -ENXIO; 6314d88a97aSDan Williams return 0; 6324d88a97aSDan Williams } 6334d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 634b354aba0SDan Williams 635b354aba0SDan Williams void __exit nvdimm_devs_exit(void) 636b354aba0SDan Williams { 637b354aba0SDan Williams ida_destroy(&dimm_ida); 638b354aba0SDan Williams } 639