145def22cSDan Williams /* 245def22cSDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 345def22cSDan Williams * 445def22cSDan Williams * This program is free software; you can redistribute it and/or modify 545def22cSDan Williams * it under the terms of version 2 of the GNU General Public License as 645def22cSDan Williams * published by the Free Software Foundation. 745def22cSDan Williams * 845def22cSDan Williams * This program is distributed in the hope that it will be useful, but 945def22cSDan Williams * WITHOUT ANY WARRANTY; without even the implied warranty of 1045def22cSDan Williams * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1145def22cSDan Williams * General Public License for more details. 1245def22cSDan Williams */ 1345def22cSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14aa9ad44aSDave Jiang #include <linux/libnvdimm.h> 150930a750SVishal Verma #include <linux/sched/mm.h> 1662232e45SDan Williams #include <linux/vmalloc.h> 1745def22cSDan Williams #include <linux/uaccess.h> 183d88002eSDan Williams #include <linux/module.h> 198c2f7e86SDan Williams #include <linux/blkdev.h> 2045def22cSDan Williams #include <linux/fcntl.h> 21e6dfb2deSDan Williams #include <linux/async.h> 228c2f7e86SDan Williams #include <linux/genhd.h> 2362232e45SDan Williams #include <linux/ndctl.h> 244d88a97aSDan Williams #include <linux/sched.h> 2545def22cSDan Williams #include <linux/slab.h> 2645def22cSDan Williams #include <linux/fs.h> 2745def22cSDan Williams #include <linux/io.h> 2862232e45SDan Williams #include <linux/mm.h> 294d88a97aSDan Williams #include <linux/nd.h> 3045def22cSDan Williams #include "nd-core.h" 314d88a97aSDan Williams #include "nd.h" 32006358b3SDave Jiang #include "pfn.h" 3345def22cSDan Williams 3462232e45SDan Williams int nvdimm_major; 3545def22cSDan Williams static int nvdimm_bus_major; 3645def22cSDan Williams static struct class *nd_class; 3718515942SDan Williams static DEFINE_IDA(nd_ida); 3845def22cSDan Williams 394d88a97aSDan Williams static int to_nd_device_type(struct device *dev) 404d88a97aSDan Williams { 414d88a97aSDan Williams if (is_nvdimm(dev)) 424d88a97aSDan Williams return ND_DEVICE_DIMM; 43c9e582aaSDan Williams else if (is_memory(dev)) 443d88002eSDan Williams return ND_DEVICE_REGION_PMEM; 453d88002eSDan Williams else if (is_nd_blk(dev)) 463d88002eSDan Williams return ND_DEVICE_REGION_BLK; 47cd03412aSDan Williams else if (is_nd_dax(dev)) 48cd03412aSDan Williams return ND_DEVICE_DAX_PMEM; 49c9e582aaSDan Williams else if (is_nd_region(dev->parent)) 503d88002eSDan Williams return nd_region_to_nstype(to_nd_region(dev->parent)); 514d88a97aSDan Williams 524d88a97aSDan Williams return 0; 534d88a97aSDan Williams } 544d88a97aSDan Williams 554d88a97aSDan Williams static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env) 564d88a97aSDan Williams { 574d88a97aSDan Williams return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT, 584d88a97aSDan Williams to_nd_device_type(dev)); 594d88a97aSDan Williams } 604d88a97aSDan Williams 613d88002eSDan Williams static struct module *to_bus_provider(struct device *dev) 623d88002eSDan Williams { 633d88002eSDan Williams /* pin bus providers while regions are enabled */ 64c9e582aaSDan Williams if (is_nd_region(dev)) { 653d88002eSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 663d88002eSDan Williams 67bc9775d8SDan Williams return nvdimm_bus->nd_desc->module; 683d88002eSDan Williams } 693d88002eSDan Williams return NULL; 703d88002eSDan Williams } 713d88002eSDan Williams 72eaf96153SDan Williams static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus) 73eaf96153SDan Williams { 74eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 75eaf96153SDan Williams nvdimm_bus->probe_active++; 76eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 77eaf96153SDan Williams } 78eaf96153SDan Williams 79eaf96153SDan Williams static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus) 80eaf96153SDan Williams { 81eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 82eaf96153SDan Williams if (--nvdimm_bus->probe_active == 0) 83eaf96153SDan Williams wake_up(&nvdimm_bus->probe_wait); 84eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 85eaf96153SDan Williams } 86eaf96153SDan Williams 874d88a97aSDan Williams static int nvdimm_bus_probe(struct device *dev) 884d88a97aSDan Williams { 894d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 903d88002eSDan Williams struct module *provider = to_bus_provider(dev); 914d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 924d88a97aSDan Williams int rc; 934d88a97aSDan Williams 943d88002eSDan Williams if (!try_module_get(provider)) 953d88002eSDan Williams return -ENXIO; 963d88002eSDan Williams 973f46833dSDan Williams dev_dbg(&nvdimm_bus->dev, "START: %s.probe(%s)\n", 983f46833dSDan Williams dev->driver->name, dev_name(dev)); 993f46833dSDan Williams 100eaf96153SDan Williams nvdimm_bus_probe_start(nvdimm_bus); 1014d88a97aSDan Williams rc = nd_drv->probe(dev); 102eaf96153SDan Williams if (rc == 0) 103eaf96153SDan Williams nd_region_probe_success(nvdimm_bus, dev); 104bf9bccc1SDan Williams else 105bf9bccc1SDan Williams nd_region_disable(nvdimm_bus, dev); 106eaf96153SDan Williams nvdimm_bus_probe_end(nvdimm_bus); 107eaf96153SDan Williams 1083f46833dSDan Williams dev_dbg(&nvdimm_bus->dev, "END: %s.probe(%s) = %d\n", dev->driver->name, 1094d88a97aSDan Williams dev_name(dev), rc); 1108c2f7e86SDan Williams 1113d88002eSDan Williams if (rc != 0) 1123d88002eSDan Williams module_put(provider); 1134d88a97aSDan Williams return rc; 1144d88a97aSDan Williams } 1154d88a97aSDan Williams 1164d88a97aSDan Williams static int nvdimm_bus_remove(struct device *dev) 1174d88a97aSDan Williams { 1184d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 1193d88002eSDan Williams struct module *provider = to_bus_provider(dev); 1204d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 1216cf9c5baSDan Williams int rc = 0; 1224d88a97aSDan Williams 1236cf9c5baSDan Williams if (nd_drv->remove) 1244d88a97aSDan Williams rc = nd_drv->remove(dev); 125eaf96153SDan Williams nd_region_disable(nvdimm_bus, dev); 126eaf96153SDan Williams 1274d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name, 1284d88a97aSDan Williams dev_name(dev), rc); 1293d88002eSDan Williams module_put(provider); 1304d88a97aSDan Williams return rc; 1314d88a97aSDan Williams } 1324d88a97aSDan Williams 133476f848aSDan Williams static void nvdimm_bus_shutdown(struct device *dev) 134476f848aSDan Williams { 135476f848aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 136476f848aSDan Williams struct nd_device_driver *nd_drv = NULL; 137476f848aSDan Williams 138476f848aSDan Williams if (dev->driver) 139476f848aSDan Williams nd_drv = to_nd_device_driver(dev->driver); 140476f848aSDan Williams 141476f848aSDan Williams if (nd_drv && nd_drv->shutdown) { 142476f848aSDan Williams nd_drv->shutdown(dev); 143476f848aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.shutdown(%s)\n", 144476f848aSDan Williams dev->driver->name, dev_name(dev)); 145476f848aSDan Williams } 146476f848aSDan Williams } 147476f848aSDan Williams 14871999466SDan Williams void nd_device_notify(struct device *dev, enum nvdimm_event event) 14971999466SDan Williams { 15071999466SDan Williams device_lock(dev); 15171999466SDan Williams if (dev->driver) { 15271999466SDan Williams struct nd_device_driver *nd_drv; 15371999466SDan Williams 15471999466SDan Williams nd_drv = to_nd_device_driver(dev->driver); 15571999466SDan Williams if (nd_drv->notify) 15671999466SDan Williams nd_drv->notify(dev, event); 15771999466SDan Williams } 15871999466SDan Williams device_unlock(dev); 15971999466SDan Williams } 16071999466SDan Williams EXPORT_SYMBOL(nd_device_notify); 16171999466SDan Williams 16271999466SDan Williams void nvdimm_region_notify(struct nd_region *nd_region, enum nvdimm_event event) 16371999466SDan Williams { 16471999466SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 16571999466SDan Williams 16671999466SDan Williams if (!nvdimm_bus) 16771999466SDan Williams return; 16871999466SDan Williams 16971999466SDan Williams /* caller is responsible for holding a reference on the device */ 17071999466SDan Williams nd_device_notify(&nd_region->dev, event); 17171999466SDan Williams } 17271999466SDan Williams EXPORT_SYMBOL_GPL(nvdimm_region_notify); 17371999466SDan Williams 17423f49844SDan Williams struct clear_badblocks_context { 17523f49844SDan Williams resource_size_t phys, cleared; 17623f49844SDan Williams }; 17723f49844SDan Williams 17823f49844SDan Williams static int nvdimm_clear_badblocks_region(struct device *dev, void *data) 17923f49844SDan Williams { 18023f49844SDan Williams struct clear_badblocks_context *ctx = data; 18123f49844SDan Williams struct nd_region *nd_region; 18223f49844SDan Williams resource_size_t ndr_end; 18323f49844SDan Williams sector_t sector; 18423f49844SDan Williams 18523f49844SDan Williams /* make sure device is a region */ 18623f49844SDan Williams if (!is_nd_pmem(dev)) 18723f49844SDan Williams return 0; 18823f49844SDan Williams 18923f49844SDan Williams nd_region = to_nd_region(dev); 19023f49844SDan Williams ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1; 19123f49844SDan Williams 19223f49844SDan Williams /* make sure we are in the region */ 19323f49844SDan Williams if (ctx->phys < nd_region->ndr_start 19423f49844SDan Williams || (ctx->phys + ctx->cleared) > ndr_end) 19523f49844SDan Williams return 0; 19623f49844SDan Williams 19723f49844SDan Williams sector = (ctx->phys - nd_region->ndr_start) / 512; 19823f49844SDan Williams badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512); 19923f49844SDan Williams 200975750a9SToshi Kani if (nd_region->bb_state) 201975750a9SToshi Kani sysfs_notify_dirent(nd_region->bb_state); 202975750a9SToshi Kani 20323f49844SDan Williams return 0; 20423f49844SDan Williams } 20523f49844SDan Williams 20623f49844SDan Williams static void nvdimm_clear_badblocks_regions(struct nvdimm_bus *nvdimm_bus, 20723f49844SDan Williams phys_addr_t phys, u64 cleared) 20823f49844SDan Williams { 20923f49844SDan Williams struct clear_badblocks_context ctx = { 21023f49844SDan Williams .phys = phys, 21123f49844SDan Williams .cleared = cleared, 21223f49844SDan Williams }; 21323f49844SDan Williams 21423f49844SDan Williams device_for_each_child(&nvdimm_bus->dev, &ctx, 21523f49844SDan Williams nvdimm_clear_badblocks_region); 21623f49844SDan Williams } 21723f49844SDan Williams 21823f49844SDan Williams static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus, 21923f49844SDan Williams phys_addr_t phys, u64 cleared) 22023f49844SDan Williams { 22123f49844SDan Williams if (cleared > 0) 222aa9ad44aSDave Jiang badrange_forget(&nvdimm_bus->badrange, phys, cleared); 22323f49844SDan Williams 22423f49844SDan Williams if (cleared > 0 && cleared / 512) 22523f49844SDan Williams nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared); 22623f49844SDan Williams } 22723f49844SDan Williams 22859e64739SDan Williams long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, 22959e64739SDan Williams unsigned int len) 23059e64739SDan Williams { 23159e64739SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 23259e64739SDan Williams struct nvdimm_bus_descriptor *nd_desc; 23359e64739SDan Williams struct nd_cmd_clear_error clear_err; 23459e64739SDan Williams struct nd_cmd_ars_cap ars_cap; 23559e64739SDan Williams u32 clear_err_unit, mask; 2360930a750SVishal Verma unsigned int noio_flag; 23759e64739SDan Williams int cmd_rc, rc; 23859e64739SDan Williams 23959e64739SDan Williams if (!nvdimm_bus) 24059e64739SDan Williams return -ENXIO; 24159e64739SDan Williams 24259e64739SDan Williams nd_desc = nvdimm_bus->nd_desc; 2431e8b8d96SDave Jiang /* 2441e8b8d96SDave Jiang * if ndctl does not exist, it's PMEM_LEGACY and 2451e8b8d96SDave Jiang * we want to just pretend everything is handled. 2461e8b8d96SDave Jiang */ 24759e64739SDan Williams if (!nd_desc->ndctl) 2481e8b8d96SDave Jiang return len; 24959e64739SDan Williams 25059e64739SDan Williams memset(&ars_cap, 0, sizeof(ars_cap)); 25159e64739SDan Williams ars_cap.address = phys; 25259e64739SDan Williams ars_cap.length = len; 2530930a750SVishal Verma noio_flag = memalloc_noio_save(); 25459e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap, 25559e64739SDan Williams sizeof(ars_cap), &cmd_rc); 2560930a750SVishal Verma memalloc_noio_restore(noio_flag); 25759e64739SDan Williams if (rc < 0) 25859e64739SDan Williams return rc; 25959e64739SDan Williams if (cmd_rc < 0) 26059e64739SDan Williams return cmd_rc; 26159e64739SDan Williams clear_err_unit = ars_cap.clear_err_unit; 26259e64739SDan Williams if (!clear_err_unit || !is_power_of_2(clear_err_unit)) 26359e64739SDan Williams return -ENXIO; 26459e64739SDan Williams 26559e64739SDan Williams mask = clear_err_unit - 1; 26659e64739SDan Williams if ((phys | len) & mask) 26759e64739SDan Williams return -ENXIO; 26859e64739SDan Williams memset(&clear_err, 0, sizeof(clear_err)); 26959e64739SDan Williams clear_err.address = phys; 27059e64739SDan Williams clear_err.length = len; 2710930a750SVishal Verma noio_flag = memalloc_noio_save(); 27259e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err, 27359e64739SDan Williams sizeof(clear_err), &cmd_rc); 2740930a750SVishal Verma memalloc_noio_restore(noio_flag); 27559e64739SDan Williams if (rc < 0) 27659e64739SDan Williams return rc; 27759e64739SDan Williams if (cmd_rc < 0) 27859e64739SDan Williams return cmd_rc; 279e046114aSVishal Verma 28023f49844SDan Williams nvdimm_account_cleared_poison(nvdimm_bus, phys, clear_err.cleared); 2818d13c029SToshi Kani 28259e64739SDan Williams return clear_err.cleared; 28359e64739SDan Williams } 28459e64739SDan Williams EXPORT_SYMBOL_GPL(nvdimm_clear_poison); 28559e64739SDan Williams 28618515942SDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv); 28718515942SDan Williams 2884d88a97aSDan Williams static struct bus_type nvdimm_bus_type = { 289e6dfb2deSDan Williams .name = "nd", 2904d88a97aSDan Williams .uevent = nvdimm_bus_uevent, 2914d88a97aSDan Williams .match = nvdimm_bus_match, 2924d88a97aSDan Williams .probe = nvdimm_bus_probe, 2934d88a97aSDan Williams .remove = nvdimm_bus_remove, 294476f848aSDan Williams .shutdown = nvdimm_bus_shutdown, 295e6dfb2deSDan Williams }; 296e6dfb2deSDan Williams 29718515942SDan Williams static void nvdimm_bus_release(struct device *dev) 29818515942SDan Williams { 29918515942SDan Williams struct nvdimm_bus *nvdimm_bus; 30018515942SDan Williams 30118515942SDan Williams nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 30218515942SDan Williams ida_simple_remove(&nd_ida, nvdimm_bus->id); 30318515942SDan Williams kfree(nvdimm_bus); 30418515942SDan Williams } 30518515942SDan Williams 30618515942SDan Williams static bool is_nvdimm_bus(struct device *dev) 30718515942SDan Williams { 30818515942SDan Williams return dev->release == nvdimm_bus_release; 30918515942SDan Williams } 31018515942SDan Williams 31118515942SDan Williams struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) 31218515942SDan Williams { 31318515942SDan Williams struct device *dev; 31418515942SDan Williams 31518515942SDan Williams for (dev = nd_dev; dev; dev = dev->parent) 31618515942SDan Williams if (is_nvdimm_bus(dev)) 31718515942SDan Williams break; 31818515942SDan Williams dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); 31918515942SDan Williams if (dev) 32018515942SDan Williams return to_nvdimm_bus(dev); 32118515942SDan Williams return NULL; 32218515942SDan Williams } 32318515942SDan Williams 32418515942SDan Williams struct nvdimm_bus *to_nvdimm_bus(struct device *dev) 32518515942SDan Williams { 32618515942SDan Williams struct nvdimm_bus *nvdimm_bus; 32718515942SDan Williams 32818515942SDan Williams nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 32918515942SDan Williams WARN_ON(!is_nvdimm_bus(dev)); 33018515942SDan Williams return nvdimm_bus; 33118515942SDan Williams } 33218515942SDan Williams EXPORT_SYMBOL_GPL(to_nvdimm_bus); 33318515942SDan Williams 33418515942SDan Williams struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 33518515942SDan Williams struct nvdimm_bus_descriptor *nd_desc) 33618515942SDan Williams { 33718515942SDan Williams struct nvdimm_bus *nvdimm_bus; 33818515942SDan Williams int rc; 33918515942SDan Williams 34018515942SDan Williams nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); 34118515942SDan Williams if (!nvdimm_bus) 34218515942SDan Williams return NULL; 34318515942SDan Williams INIT_LIST_HEAD(&nvdimm_bus->list); 34418515942SDan Williams INIT_LIST_HEAD(&nvdimm_bus->mapping_list); 34518515942SDan Williams init_waitqueue_head(&nvdimm_bus->probe_wait); 34618515942SDan Williams nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 34718515942SDan Williams mutex_init(&nvdimm_bus->reconfig_mutex); 348aa9ad44aSDave Jiang badrange_init(&nvdimm_bus->badrange); 34918515942SDan Williams if (nvdimm_bus->id < 0) { 35018515942SDan Williams kfree(nvdimm_bus); 35118515942SDan Williams return NULL; 35218515942SDan Williams } 35318515942SDan Williams nvdimm_bus->nd_desc = nd_desc; 35418515942SDan Williams nvdimm_bus->dev.parent = parent; 35518515942SDan Williams nvdimm_bus->dev.release = nvdimm_bus_release; 35618515942SDan Williams nvdimm_bus->dev.groups = nd_desc->attr_groups; 35718515942SDan Williams nvdimm_bus->dev.bus = &nvdimm_bus_type; 3581ff19f48SOliver O'Halloran nvdimm_bus->dev.of_node = nd_desc->of_node; 35918515942SDan Williams dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); 36018515942SDan Williams rc = device_register(&nvdimm_bus->dev); 36118515942SDan Williams if (rc) { 36218515942SDan Williams dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); 36318515942SDan Williams goto err; 36418515942SDan Williams } 36518515942SDan Williams 36618515942SDan Williams return nvdimm_bus; 36718515942SDan Williams err: 36818515942SDan Williams put_device(&nvdimm_bus->dev); 36918515942SDan Williams return NULL; 37018515942SDan Williams } 37118515942SDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_register); 37218515942SDan Williams 37318515942SDan Williams void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) 37418515942SDan Williams { 37518515942SDan Williams if (!nvdimm_bus) 37618515942SDan Williams return; 37718515942SDan Williams device_unregister(&nvdimm_bus->dev); 37818515942SDan Williams } 37918515942SDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); 38018515942SDan Williams 38118515942SDan Williams static int child_unregister(struct device *dev, void *data) 38218515942SDan Williams { 38318515942SDan Williams /* 38418515942SDan Williams * the singular ndctl class device per bus needs to be 38518515942SDan Williams * "device_destroy"ed, so skip it here 38618515942SDan Williams * 38718515942SDan Williams * i.e. remove classless children 38818515942SDan Williams */ 38918515942SDan Williams if (dev->class) 39018515942SDan Williams /* pass */; 39118515942SDan Williams else 39218515942SDan Williams nd_device_unregister(dev, ND_SYNC); 39318515942SDan Williams return 0; 39418515942SDan Williams } 39518515942SDan Williams 396aa9ad44aSDave Jiang static void free_badrange_list(struct list_head *badrange_list) 39718515942SDan Williams { 398aa9ad44aSDave Jiang struct badrange_entry *bre, *next; 39918515942SDan Williams 400aa9ad44aSDave Jiang list_for_each_entry_safe(bre, next, badrange_list, list) { 401aa9ad44aSDave Jiang list_del(&bre->list); 402aa9ad44aSDave Jiang kfree(bre); 40318515942SDan Williams } 404aa9ad44aSDave Jiang list_del_init(badrange_list); 40518515942SDan Williams } 40618515942SDan Williams 40718515942SDan Williams static int nd_bus_remove(struct device *dev) 40818515942SDan Williams { 40918515942SDan Williams struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 41018515942SDan Williams 41118515942SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 41218515942SDan Williams list_del_init(&nvdimm_bus->list); 41318515942SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 41418515942SDan Williams 41518515942SDan Williams nd_synchronize(); 41618515942SDan Williams device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); 41718515942SDan Williams 418aa9ad44aSDave Jiang spin_lock(&nvdimm_bus->badrange.lock); 419aa9ad44aSDave Jiang free_badrange_list(&nvdimm_bus->badrange.list); 420aa9ad44aSDave Jiang spin_unlock(&nvdimm_bus->badrange.lock); 42118515942SDan Williams 42218515942SDan Williams nvdimm_bus_destroy_ndctl(nvdimm_bus); 42318515942SDan Williams 42418515942SDan Williams return 0; 42518515942SDan Williams } 42618515942SDan Williams 42718515942SDan Williams static int nd_bus_probe(struct device *dev) 42818515942SDan Williams { 42918515942SDan Williams struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 43018515942SDan Williams int rc; 43118515942SDan Williams 43218515942SDan Williams rc = nvdimm_bus_create_ndctl(nvdimm_bus); 43318515942SDan Williams if (rc) 43418515942SDan Williams return rc; 43518515942SDan Williams 43618515942SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 43718515942SDan Williams list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list); 43818515942SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 43918515942SDan Williams 44018515942SDan Williams /* enable bus provider attributes to look up their local context */ 44118515942SDan Williams dev_set_drvdata(dev, nvdimm_bus->nd_desc); 44218515942SDan Williams 44318515942SDan Williams return 0; 44418515942SDan Williams } 44518515942SDan Williams 44618515942SDan Williams static struct nd_device_driver nd_bus_driver = { 44718515942SDan Williams .probe = nd_bus_probe, 44818515942SDan Williams .remove = nd_bus_remove, 44918515942SDan Williams .drv = { 45018515942SDan Williams .name = "nd_bus", 45118515942SDan Williams .suppress_bind_attrs = true, 45218515942SDan Williams .bus = &nvdimm_bus_type, 45318515942SDan Williams .owner = THIS_MODULE, 45418515942SDan Williams .mod_name = KBUILD_MODNAME, 45518515942SDan Williams }, 45618515942SDan Williams }; 45718515942SDan Williams 45818515942SDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 45918515942SDan Williams { 46018515942SDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 46118515942SDan Williams 46218515942SDan Williams if (is_nvdimm_bus(dev) && nd_drv == &nd_bus_driver) 46318515942SDan Williams return true; 46418515942SDan Williams 46518515942SDan Williams return !!test_bit(to_nd_device_type(dev), &nd_drv->type); 46618515942SDan Williams } 46718515942SDan Williams 4684d88a97aSDan Williams static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 4694d88a97aSDan Williams 4704d88a97aSDan Williams void nd_synchronize(void) 4714d88a97aSDan Williams { 4724d88a97aSDan Williams async_synchronize_full_domain(&nd_async_domain); 4734d88a97aSDan Williams } 4744d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_synchronize); 4754d88a97aSDan Williams 4764d88a97aSDan Williams static void nd_async_device_register(void *d, async_cookie_t cookie) 4774d88a97aSDan Williams { 4784d88a97aSDan Williams struct device *dev = d; 4794d88a97aSDan Williams 4804d88a97aSDan Williams if (device_add(dev) != 0) { 4814d88a97aSDan Williams dev_err(dev, "%s: failed\n", __func__); 4824d88a97aSDan Williams put_device(dev); 4834d88a97aSDan Williams } 4844d88a97aSDan Williams put_device(dev); 485b6eae0f6SAlexander Duyck if (dev->parent) 486b6eae0f6SAlexander Duyck put_device(dev->parent); 4874d88a97aSDan Williams } 4884d88a97aSDan Williams 4894d88a97aSDan Williams static void nd_async_device_unregister(void *d, async_cookie_t cookie) 4904d88a97aSDan Williams { 4914d88a97aSDan Williams struct device *dev = d; 4924d88a97aSDan Williams 4930ba1c634SDan Williams /* flush bus operations before delete */ 4940ba1c634SDan Williams nvdimm_bus_lock(dev); 4950ba1c634SDan Williams nvdimm_bus_unlock(dev); 4960ba1c634SDan Williams 4974d88a97aSDan Williams device_unregister(dev); 4984d88a97aSDan Williams put_device(dev); 4994d88a97aSDan Williams } 5004d88a97aSDan Williams 5018c2f7e86SDan Williams void __nd_device_register(struct device *dev) 5024d88a97aSDan Williams { 503cd03412aSDan Williams if (!dev) 504cd03412aSDan Williams return; 5051a091d16SAlexander Duyck 5061a091d16SAlexander Duyck /* 5071a091d16SAlexander Duyck * Ensure that region devices always have their NUMA node set as 5081a091d16SAlexander Duyck * early as possible. This way we are able to make certain that 5091a091d16SAlexander Duyck * any memory associated with the creation and the creation 5101a091d16SAlexander Duyck * itself of the region is associated with the correct node. 5111a091d16SAlexander Duyck */ 5121a091d16SAlexander Duyck if (is_nd_region(dev)) 5131a091d16SAlexander Duyck set_dev_node(dev, to_nd_region(dev)->numa_node); 5141a091d16SAlexander Duyck 5154d88a97aSDan Williams dev->bus = &nvdimm_bus_type; 516b6eae0f6SAlexander Duyck if (dev->parent) 517b6eae0f6SAlexander Duyck get_device(dev->parent); 5184d88a97aSDan Williams get_device(dev); 5194d88a97aSDan Williams async_schedule_domain(nd_async_device_register, dev, 5204d88a97aSDan Williams &nd_async_domain); 5214d88a97aSDan Williams } 5228c2f7e86SDan Williams 5238c2f7e86SDan Williams void nd_device_register(struct device *dev) 5248c2f7e86SDan Williams { 5258c2f7e86SDan Williams device_initialize(dev); 5268c2f7e86SDan Williams __nd_device_register(dev); 5278c2f7e86SDan Williams } 5284d88a97aSDan Williams EXPORT_SYMBOL(nd_device_register); 5294d88a97aSDan Williams 5304d88a97aSDan Williams void nd_device_unregister(struct device *dev, enum nd_async_mode mode) 5314d88a97aSDan Williams { 5324d88a97aSDan Williams switch (mode) { 5334d88a97aSDan Williams case ND_ASYNC: 5344d88a97aSDan Williams get_device(dev); 5354d88a97aSDan Williams async_schedule_domain(nd_async_device_unregister, dev, 5364d88a97aSDan Williams &nd_async_domain); 5374d88a97aSDan Williams break; 5384d88a97aSDan Williams case ND_SYNC: 5394d88a97aSDan Williams nd_synchronize(); 5404d88a97aSDan Williams device_unregister(dev); 5414d88a97aSDan Williams break; 5424d88a97aSDan Williams } 5434d88a97aSDan Williams } 5444d88a97aSDan Williams EXPORT_SYMBOL(nd_device_unregister); 5454d88a97aSDan Williams 5464d88a97aSDan Williams /** 5474d88a97aSDan Williams * __nd_driver_register() - register a region or a namespace driver 5484d88a97aSDan Williams * @nd_drv: driver to register 5494d88a97aSDan Williams * @owner: automatically set by nd_driver_register() macro 5504d88a97aSDan Williams * @mod_name: automatically set by nd_driver_register() macro 5514d88a97aSDan Williams */ 5524d88a97aSDan Williams int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner, 5534d88a97aSDan Williams const char *mod_name) 5544d88a97aSDan Williams { 5554d88a97aSDan Williams struct device_driver *drv = &nd_drv->drv; 5564d88a97aSDan Williams 5574d88a97aSDan Williams if (!nd_drv->type) { 5584d88a97aSDan Williams pr_debug("driver type bitmask not set (%pf)\n", 5594d88a97aSDan Williams __builtin_return_address(0)); 5604d88a97aSDan Williams return -EINVAL; 5614d88a97aSDan Williams } 5624d88a97aSDan Williams 5636cf9c5baSDan Williams if (!nd_drv->probe) { 5646cf9c5baSDan Williams pr_debug("%s ->probe() must be specified\n", mod_name); 5654d88a97aSDan Williams return -EINVAL; 5664d88a97aSDan Williams } 5674d88a97aSDan Williams 5684d88a97aSDan Williams drv->bus = &nvdimm_bus_type; 5694d88a97aSDan Williams drv->owner = owner; 5704d88a97aSDan Williams drv->mod_name = mod_name; 5714d88a97aSDan Williams 5724d88a97aSDan Williams return driver_register(drv); 5734d88a97aSDan Williams } 5744d88a97aSDan Williams EXPORT_SYMBOL(__nd_driver_register); 5754d88a97aSDan Williams 57658138820SDan Williams int nvdimm_revalidate_disk(struct gendisk *disk) 57758138820SDan Williams { 57852c44d93SDan Williams struct device *dev = disk_to_dev(disk)->parent; 57958138820SDan Williams struct nd_region *nd_region = to_nd_region(dev->parent); 580254a4cd5SRobert Elliott int disk_ro = get_disk_ro(disk); 58158138820SDan Williams 582254a4cd5SRobert Elliott /* 583254a4cd5SRobert Elliott * Upgrade to read-only if the region is read-only preserve as 584254a4cd5SRobert Elliott * read-only if the disk is already read-only. 585254a4cd5SRobert Elliott */ 586254a4cd5SRobert Elliott if (disk_ro || nd_region->ro == disk_ro) 58758138820SDan Williams return 0; 58858138820SDan Williams 589254a4cd5SRobert Elliott dev_info(dev, "%s read-only, marking %s read-only\n", 590254a4cd5SRobert Elliott dev_name(&nd_region->dev), disk->disk_name); 591254a4cd5SRobert Elliott set_disk_ro(disk, 1); 59258138820SDan Williams 59358138820SDan Williams return 0; 59458138820SDan Williams 59558138820SDan Williams } 59658138820SDan Williams EXPORT_SYMBOL(nvdimm_revalidate_disk); 59758138820SDan Williams 5984d88a97aSDan Williams static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 5994d88a97aSDan Williams char *buf) 6004d88a97aSDan Williams { 6014d88a97aSDan Williams return sprintf(buf, ND_DEVICE_MODALIAS_FMT "\n", 6024d88a97aSDan Williams to_nd_device_type(dev)); 6034d88a97aSDan Williams } 6044d88a97aSDan Williams static DEVICE_ATTR_RO(modalias); 6054d88a97aSDan Williams 6064d88a97aSDan Williams static ssize_t devtype_show(struct device *dev, struct device_attribute *attr, 6074d88a97aSDan Williams char *buf) 6084d88a97aSDan Williams { 6094d88a97aSDan Williams return sprintf(buf, "%s\n", dev->type->name); 6104d88a97aSDan Williams } 6114d88a97aSDan Williams static DEVICE_ATTR_RO(devtype); 6124d88a97aSDan Williams 6134d88a97aSDan Williams static struct attribute *nd_device_attributes[] = { 6144d88a97aSDan Williams &dev_attr_modalias.attr, 6154d88a97aSDan Williams &dev_attr_devtype.attr, 6164d88a97aSDan Williams NULL, 6174d88a97aSDan Williams }; 6184d88a97aSDan Williams 6194d88a97aSDan Williams /** 6204d88a97aSDan Williams * nd_device_attribute_group - generic attributes for all devices on an nd bus 6214d88a97aSDan Williams */ 6224d88a97aSDan Williams struct attribute_group nd_device_attribute_group = { 6234d88a97aSDan Williams .attrs = nd_device_attributes, 6244d88a97aSDan Williams }; 6254d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_device_attribute_group); 6264d88a97aSDan Williams 62774ae66c3SToshi Kani static ssize_t numa_node_show(struct device *dev, 62874ae66c3SToshi Kani struct device_attribute *attr, char *buf) 62974ae66c3SToshi Kani { 63074ae66c3SToshi Kani return sprintf(buf, "%d\n", dev_to_node(dev)); 63174ae66c3SToshi Kani } 63274ae66c3SToshi Kani static DEVICE_ATTR_RO(numa_node); 63374ae66c3SToshi Kani 63474ae66c3SToshi Kani static struct attribute *nd_numa_attributes[] = { 63574ae66c3SToshi Kani &dev_attr_numa_node.attr, 63674ae66c3SToshi Kani NULL, 63774ae66c3SToshi Kani }; 63874ae66c3SToshi Kani 63974ae66c3SToshi Kani static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a, 64074ae66c3SToshi Kani int n) 64174ae66c3SToshi Kani { 64274ae66c3SToshi Kani if (!IS_ENABLED(CONFIG_NUMA)) 64374ae66c3SToshi Kani return 0; 64474ae66c3SToshi Kani 64574ae66c3SToshi Kani return a->mode; 64674ae66c3SToshi Kani } 64774ae66c3SToshi Kani 64874ae66c3SToshi Kani /** 64974ae66c3SToshi Kani * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus 65074ae66c3SToshi Kani */ 65174ae66c3SToshi Kani struct attribute_group nd_numa_attribute_group = { 65274ae66c3SToshi Kani .attrs = nd_numa_attributes, 65374ae66c3SToshi Kani .is_visible = nd_numa_attr_visible, 65474ae66c3SToshi Kani }; 65574ae66c3SToshi Kani EXPORT_SYMBOL_GPL(nd_numa_attribute_group); 65674ae66c3SToshi Kani 65745def22cSDan Williams int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) 65845def22cSDan Williams { 65945def22cSDan Williams dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id); 66045def22cSDan Williams struct device *dev; 66145def22cSDan Williams 66245def22cSDan Williams dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus, 66345def22cSDan Williams "ndctl%d", nvdimm_bus->id); 66445def22cSDan Williams 66542588958SDan Williams if (IS_ERR(dev)) 66645def22cSDan Williams dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld\n", 66745def22cSDan Williams nvdimm_bus->id, PTR_ERR(dev)); 66842588958SDan Williams return PTR_ERR_OR_ZERO(dev); 66945def22cSDan Williams } 67045def22cSDan Williams 67145def22cSDan Williams void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus) 67245def22cSDan Williams { 67345def22cSDan Williams device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id)); 67445def22cSDan Williams } 67545def22cSDan Williams 67662232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = { 67762232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 67862232e45SDan Williams [ND_CMD_SMART] = { 67962232e45SDan Williams .out_num = 2, 68021129112SDan Williams .out_sizes = { 4, 128, }, 68162232e45SDan Williams }, 68262232e45SDan Williams [ND_CMD_SMART_THRESHOLD] = { 68362232e45SDan Williams .out_num = 2, 68462232e45SDan Williams .out_sizes = { 4, 8, }, 68562232e45SDan Williams }, 68662232e45SDan Williams [ND_CMD_DIMM_FLAGS] = { 68762232e45SDan Williams .out_num = 2, 68862232e45SDan Williams .out_sizes = { 4, 4 }, 68962232e45SDan Williams }, 69062232e45SDan Williams [ND_CMD_GET_CONFIG_SIZE] = { 69162232e45SDan Williams .out_num = 3, 69262232e45SDan Williams .out_sizes = { 4, 4, 4, }, 69362232e45SDan Williams }, 69462232e45SDan Williams [ND_CMD_GET_CONFIG_DATA] = { 69562232e45SDan Williams .in_num = 2, 69662232e45SDan Williams .in_sizes = { 4, 4, }, 69762232e45SDan Williams .out_num = 2, 69862232e45SDan Williams .out_sizes = { 4, UINT_MAX, }, 69962232e45SDan Williams }, 70062232e45SDan Williams [ND_CMD_SET_CONFIG_DATA] = { 70162232e45SDan Williams .in_num = 3, 70262232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 70362232e45SDan Williams .out_num = 1, 70462232e45SDan Williams .out_sizes = { 4, }, 70562232e45SDan Williams }, 70662232e45SDan Williams [ND_CMD_VENDOR] = { 70762232e45SDan Williams .in_num = 3, 70862232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 70962232e45SDan Williams .out_num = 3, 71062232e45SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 71162232e45SDan Williams }, 71231eca76bSDan Williams [ND_CMD_CALL] = { 71331eca76bSDan Williams .in_num = 2, 71431eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 71531eca76bSDan Williams .out_num = 1, 71631eca76bSDan Williams .out_sizes = { UINT_MAX, }, 71731eca76bSDan Williams }, 71862232e45SDan Williams }; 71962232e45SDan Williams 72062232e45SDan Williams const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) 72162232e45SDan Williams { 72262232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_dimm_descs)) 72362232e45SDan Williams return &__nd_cmd_dimm_descs[cmd]; 72462232e45SDan Williams return NULL; 72562232e45SDan Williams } 72662232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_dimm_desc); 72762232e45SDan Williams 72862232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { 72962232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 73062232e45SDan Williams [ND_CMD_ARS_CAP] = { 73162232e45SDan Williams .in_num = 2, 73262232e45SDan Williams .in_sizes = { 8, 8, }, 7334577b066SDan Williams .out_num = 4, 7344577b066SDan Williams .out_sizes = { 4, 4, 4, 4, }, 73562232e45SDan Williams }, 73662232e45SDan Williams [ND_CMD_ARS_START] = { 7374577b066SDan Williams .in_num = 5, 7384577b066SDan Williams .in_sizes = { 8, 8, 2, 1, 5, }, 7394577b066SDan Williams .out_num = 2, 7404577b066SDan Williams .out_sizes = { 4, 4, }, 74162232e45SDan Williams }, 74262232e45SDan Williams [ND_CMD_ARS_STATUS] = { 743747ffe11SDan Williams .out_num = 3, 744747ffe11SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 74562232e45SDan Williams }, 746d4f32367SDan Williams [ND_CMD_CLEAR_ERROR] = { 747d4f32367SDan Williams .in_num = 2, 748d4f32367SDan Williams .in_sizes = { 8, 8, }, 749d4f32367SDan Williams .out_num = 3, 750d4f32367SDan Williams .out_sizes = { 4, 4, 8, }, 751d4f32367SDan Williams }, 75231eca76bSDan Williams [ND_CMD_CALL] = { 75331eca76bSDan Williams .in_num = 2, 75431eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 75531eca76bSDan Williams .out_num = 1, 75631eca76bSDan Williams .out_sizes = { UINT_MAX, }, 75731eca76bSDan Williams }, 75862232e45SDan Williams }; 75962232e45SDan Williams 76062232e45SDan Williams const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) 76162232e45SDan Williams { 76262232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_bus_descs)) 76362232e45SDan Williams return &__nd_cmd_bus_descs[cmd]; 76462232e45SDan Williams return NULL; 76562232e45SDan Williams } 76662232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_bus_desc); 76762232e45SDan Williams 76862232e45SDan Williams u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, 76962232e45SDan Williams const struct nd_cmd_desc *desc, int idx, void *buf) 77062232e45SDan Williams { 77162232e45SDan Williams if (idx >= desc->in_num) 77262232e45SDan Williams return UINT_MAX; 77362232e45SDan Williams 77462232e45SDan Williams if (desc->in_sizes[idx] < UINT_MAX) 77562232e45SDan Williams return desc->in_sizes[idx]; 77662232e45SDan Williams 77762232e45SDan Williams if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA && idx == 2) { 77862232e45SDan Williams struct nd_cmd_set_config_hdr *hdr = buf; 77962232e45SDan Williams 78062232e45SDan Williams return hdr->in_length; 78162232e45SDan Williams } else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) { 78262232e45SDan Williams struct nd_cmd_vendor_hdr *hdr = buf; 78362232e45SDan Williams 78462232e45SDan Williams return hdr->in_length; 78531eca76bSDan Williams } else if (cmd == ND_CMD_CALL) { 78631eca76bSDan Williams struct nd_cmd_pkg *pkg = buf; 78731eca76bSDan Williams 78831eca76bSDan Williams return pkg->nd_size_in; 78962232e45SDan Williams } 79062232e45SDan Williams 79162232e45SDan Williams return UINT_MAX; 79262232e45SDan Williams } 79362232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_in_size); 79462232e45SDan Williams 79562232e45SDan Williams u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd, 79662232e45SDan Williams const struct nd_cmd_desc *desc, int idx, const u32 *in_field, 797efda1b5dSDan Williams const u32 *out_field, unsigned long remainder) 79862232e45SDan Williams { 79962232e45SDan Williams if (idx >= desc->out_num) 80062232e45SDan Williams return UINT_MAX; 80162232e45SDan Williams 80262232e45SDan Williams if (desc->out_sizes[idx] < UINT_MAX) 80362232e45SDan Williams return desc->out_sizes[idx]; 80462232e45SDan Williams 80562232e45SDan Williams if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && idx == 1) 80662232e45SDan Williams return in_field[1]; 80762232e45SDan Williams else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) 80862232e45SDan Williams return out_field[1]; 809efda1b5dSDan Williams else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) { 810efda1b5dSDan Williams /* 811efda1b5dSDan Williams * Per table 9-276 ARS Data in ACPI 6.1, out_field[1] is 812efda1b5dSDan Williams * "Size of Output Buffer in bytes, including this 813efda1b5dSDan Williams * field." 814efda1b5dSDan Williams */ 815efda1b5dSDan Williams if (out_field[1] < 4) 816efda1b5dSDan Williams return 0; 817efda1b5dSDan Williams /* 818efda1b5dSDan Williams * ACPI 6.1 is ambiguous if 'status' is included in the 819efda1b5dSDan Williams * output size. If we encounter an output size that 820efda1b5dSDan Williams * overshoots the remainder by 4 bytes, assume it was 821efda1b5dSDan Williams * including 'status'. 822efda1b5dSDan Williams */ 823286e8771SVishal Verma if (out_field[1] - 4 == remainder) 824efda1b5dSDan Williams return remainder; 825286e8771SVishal Verma return out_field[1] - 8; 826efda1b5dSDan Williams } else if (cmd == ND_CMD_CALL) { 82731eca76bSDan Williams struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field; 82831eca76bSDan Williams 82931eca76bSDan Williams return pkg->nd_size_out; 83031eca76bSDan Williams } 83131eca76bSDan Williams 83262232e45SDan Williams 83362232e45SDan Williams return UINT_MAX; 83462232e45SDan Williams } 83562232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_out_size); 83662232e45SDan Williams 837bf9bccc1SDan Williams void wait_nvdimm_bus_probe_idle(struct device *dev) 838eaf96153SDan Williams { 839bf9bccc1SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 840bf9bccc1SDan Williams 841eaf96153SDan Williams do { 842eaf96153SDan Williams if (nvdimm_bus->probe_active == 0) 843eaf96153SDan Williams break; 844eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 845eaf96153SDan Williams wait_event(nvdimm_bus->probe_wait, 846eaf96153SDan Williams nvdimm_bus->probe_active == 0); 847eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 848eaf96153SDan Williams } while (true); 849eaf96153SDan Williams } 850eaf96153SDan Williams 851006358b3SDave Jiang static int nd_pmem_forget_poison_check(struct device *dev, void *data) 852d4f32367SDan Williams { 853006358b3SDave Jiang struct nd_cmd_clear_error *clear_err = 854006358b3SDave Jiang (struct nd_cmd_clear_error *)data; 855006358b3SDave Jiang struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; 856006358b3SDave Jiang struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL; 857006358b3SDave Jiang struct nd_dax *nd_dax = is_nd_dax(dev) ? to_nd_dax(dev) : NULL; 858006358b3SDave Jiang struct nd_namespace_common *ndns = NULL; 859006358b3SDave Jiang struct nd_namespace_io *nsio; 860006358b3SDave Jiang resource_size_t offset = 0, end_trunc = 0, start, end, pstart, pend; 861006358b3SDave Jiang 862006358b3SDave Jiang if (nd_dax || !dev->driver) 863d4f32367SDan Williams return 0; 864006358b3SDave Jiang 865006358b3SDave Jiang start = clear_err->address; 866006358b3SDave Jiang end = clear_err->address + clear_err->cleared - 1; 867006358b3SDave Jiang 868006358b3SDave Jiang if (nd_btt || nd_pfn || nd_dax) { 869006358b3SDave Jiang if (nd_btt) 870006358b3SDave Jiang ndns = nd_btt->ndns; 871006358b3SDave Jiang else if (nd_pfn) 872006358b3SDave Jiang ndns = nd_pfn->ndns; 873006358b3SDave Jiang else if (nd_dax) 874006358b3SDave Jiang ndns = nd_dax->nd_pfn.ndns; 875006358b3SDave Jiang 876006358b3SDave Jiang if (!ndns) 877006358b3SDave Jiang return 0; 878006358b3SDave Jiang } else 879006358b3SDave Jiang ndns = to_ndns(dev); 880006358b3SDave Jiang 881006358b3SDave Jiang nsio = to_nd_namespace_io(&ndns->dev); 882006358b3SDave Jiang pstart = nsio->res.start + offset; 883006358b3SDave Jiang pend = nsio->res.end - end_trunc; 884006358b3SDave Jiang 885006358b3SDave Jiang if ((pstart >= start) && (pend <= end)) 886006358b3SDave Jiang return -EBUSY; 887006358b3SDave Jiang 888006358b3SDave Jiang return 0; 889006358b3SDave Jiang 890006358b3SDave Jiang } 891006358b3SDave Jiang 892006358b3SDave Jiang static int nd_ns_forget_poison_check(struct device *dev, void *data) 893006358b3SDave Jiang { 894006358b3SDave Jiang return device_for_each_child(dev, data, nd_pmem_forget_poison_check); 895d4f32367SDan Williams } 896d4f32367SDan Williams 897eaf96153SDan Williams /* set_config requires an idle interleave set */ 89887bf572eSDan Williams static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, 899006358b3SDave Jiang struct nvdimm *nvdimm, unsigned int cmd, void *data) 900eaf96153SDan Williams { 90187bf572eSDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 90287bf572eSDan Williams 90387bf572eSDan Williams /* ask the bus provider if it would like to block this request */ 90487bf572eSDan Williams if (nd_desc->clear_to_send) { 905b3ed2ce0SDave Jiang int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd, data); 90687bf572eSDan Williams 90787bf572eSDan Williams if (rc) 90887bf572eSDan Williams return rc; 90987bf572eSDan Williams } 910eaf96153SDan Williams 911d4f32367SDan Williams /* require clear error to go through the pmem driver */ 912d4f32367SDan Williams if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR) 913006358b3SDave Jiang return device_for_each_child(&nvdimm_bus->dev, data, 914006358b3SDave Jiang nd_ns_forget_poison_check); 915d4f32367SDan Williams 916eaf96153SDan Williams if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) 917eaf96153SDan Williams return 0; 918eaf96153SDan Williams 91987bf572eSDan Williams /* prevent label manipulation while the kernel owns label updates */ 920bf9bccc1SDan Williams wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev); 921eaf96153SDan Williams if (atomic_read(&nvdimm->busy)) 922eaf96153SDan Williams return -EBUSY; 923eaf96153SDan Williams return 0; 924eaf96153SDan Williams } 925eaf96153SDan Williams 92662232e45SDan Williams static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 92762232e45SDan Williams int read_only, unsigned int ioctl_cmd, unsigned long arg) 92862232e45SDan Williams { 92962232e45SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 93062232e45SDan Williams static char out_env[ND_CMD_MAX_ENVELOPE]; 93162232e45SDan Williams static char in_env[ND_CMD_MAX_ENVELOPE]; 93262232e45SDan Williams const struct nd_cmd_desc *desc = NULL; 93362232e45SDan Williams unsigned int cmd = _IOC_NR(ioctl_cmd); 93462232e45SDan Williams struct device *dev = &nvdimm_bus->dev; 93558738c49SDan Williams void __user *p = (void __user *) arg; 93662232e45SDan Williams const char *cmd_name, *dimm_name; 93758738c49SDan Williams u32 in_len = 0, out_len = 0; 93858738c49SDan Williams unsigned int func = cmd; 939e3654ecaSDan Williams unsigned long cmd_mask; 94058738c49SDan Williams struct nd_cmd_pkg pkg; 941006358b3SDave Jiang int rc, i, cmd_rc; 94258738c49SDan Williams u64 buf_len = 0; 94358738c49SDan Williams void *buf; 94462232e45SDan Williams 94562232e45SDan Williams if (nvdimm) { 94662232e45SDan Williams desc = nd_cmd_dimm_desc(cmd); 94762232e45SDan Williams cmd_name = nvdimm_cmd_name(cmd); 948e3654ecaSDan Williams cmd_mask = nvdimm->cmd_mask; 94962232e45SDan Williams dimm_name = dev_name(&nvdimm->dev); 95062232e45SDan Williams } else { 95162232e45SDan Williams desc = nd_cmd_bus_desc(cmd); 95262232e45SDan Williams cmd_name = nvdimm_bus_cmd_name(cmd); 953e3654ecaSDan Williams cmd_mask = nd_desc->cmd_mask; 95462232e45SDan Williams dimm_name = "bus"; 95562232e45SDan Williams } 95662232e45SDan Williams 95731eca76bSDan Williams if (cmd == ND_CMD_CALL) { 95831eca76bSDan Williams if (copy_from_user(&pkg, p, sizeof(pkg))) 95931eca76bSDan Williams return -EFAULT; 96031eca76bSDan Williams } 96131eca76bSDan Williams 96262232e45SDan Williams if (!desc || (desc->out_num + desc->in_num == 0) || 963e3654ecaSDan Williams !test_bit(cmd, &cmd_mask)) 96462232e45SDan Williams return -ENOTTY; 96562232e45SDan Williams 96662232e45SDan Williams /* fail write commands (when read-only) */ 96762232e45SDan Williams if (read_only) 96807accfa9SJerry Hoemann switch (cmd) { 96907accfa9SJerry Hoemann case ND_CMD_VENDOR: 97007accfa9SJerry Hoemann case ND_CMD_SET_CONFIG_DATA: 97107accfa9SJerry Hoemann case ND_CMD_ARS_START: 972d4f32367SDan Williams case ND_CMD_CLEAR_ERROR: 97331eca76bSDan Williams case ND_CMD_CALL: 97462232e45SDan Williams dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 97562232e45SDan Williams nvdimm ? nvdimm_cmd_name(cmd) 97662232e45SDan Williams : nvdimm_bus_cmd_name(cmd)); 97762232e45SDan Williams return -EPERM; 97862232e45SDan Williams default: 97962232e45SDan Williams break; 98062232e45SDan Williams } 98162232e45SDan Williams 98262232e45SDan Williams /* process an input envelope */ 98362232e45SDan Williams for (i = 0; i < desc->in_num; i++) { 98462232e45SDan Williams u32 in_size, copy; 98562232e45SDan Williams 98662232e45SDan Williams in_size = nd_cmd_in_size(nvdimm, cmd, desc, i, in_env); 98762232e45SDan Williams if (in_size == UINT_MAX) { 98862232e45SDan Williams dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n", 98962232e45SDan Williams __func__, dimm_name, cmd_name, i); 99062232e45SDan Williams return -ENXIO; 99162232e45SDan Williams } 99262232e45SDan Williams if (in_len < sizeof(in_env)) 99362232e45SDan Williams copy = min_t(u32, sizeof(in_env) - in_len, in_size); 99462232e45SDan Williams else 99562232e45SDan Williams copy = 0; 99662232e45SDan Williams if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) 99762232e45SDan Williams return -EFAULT; 99862232e45SDan Williams in_len += in_size; 99962232e45SDan Williams } 100062232e45SDan Williams 100131eca76bSDan Williams if (cmd == ND_CMD_CALL) { 100253b85a44SJerry Hoemann func = pkg.nd_command; 1003426824d6SDan Williams dev_dbg(dev, "%s, idx: %llu, in: %u, out: %u, len %llu\n", 1004426824d6SDan Williams dimm_name, pkg.nd_command, 100531eca76bSDan Williams in_len, out_len, buf_len); 100631eca76bSDan Williams } 100731eca76bSDan Williams 100862232e45SDan Williams /* process an output envelope */ 100962232e45SDan Williams for (i = 0; i < desc->out_num; i++) { 101062232e45SDan Williams u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, 1011efda1b5dSDan Williams (u32 *) in_env, (u32 *) out_env, 0); 101262232e45SDan Williams u32 copy; 101362232e45SDan Williams 101462232e45SDan Williams if (out_size == UINT_MAX) { 1015426824d6SDan Williams dev_dbg(dev, "%s unknown output size cmd: %s field: %d\n", 1016426824d6SDan Williams dimm_name, cmd_name, i); 101762232e45SDan Williams return -EFAULT; 101862232e45SDan Williams } 101962232e45SDan Williams if (out_len < sizeof(out_env)) 102062232e45SDan Williams copy = min_t(u32, sizeof(out_env) - out_len, out_size); 102162232e45SDan Williams else 102262232e45SDan Williams copy = 0; 102362232e45SDan Williams if (copy && copy_from_user(&out_env[out_len], 102462232e45SDan Williams p + in_len + out_len, copy)) 102562232e45SDan Williams return -EFAULT; 102662232e45SDan Williams out_len += out_size; 102762232e45SDan Williams } 102862232e45SDan Williams 102958738c49SDan Williams buf_len = (u64) out_len + (u64) in_len; 103062232e45SDan Williams if (buf_len > ND_IOCTL_MAX_BUFLEN) { 1031426824d6SDan Williams dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d\n", dimm_name, 1032426824d6SDan Williams cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN); 103362232e45SDan Williams return -EINVAL; 103462232e45SDan Williams } 103562232e45SDan Williams 103662232e45SDan Williams buf = vmalloc(buf_len); 103762232e45SDan Williams if (!buf) 103862232e45SDan Williams return -ENOMEM; 103962232e45SDan Williams 104062232e45SDan Williams if (copy_from_user(buf, p, buf_len)) { 104162232e45SDan Williams rc = -EFAULT; 104262232e45SDan Williams goto out; 104362232e45SDan Williams } 104462232e45SDan Williams 1045eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 104653b85a44SJerry Hoemann rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf); 1047eaf96153SDan Williams if (rc) 1048eaf96153SDan Williams goto out_unlock; 1049eaf96153SDan Williams 1050006358b3SDave Jiang rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc); 105162232e45SDan Williams if (rc < 0) 1052eaf96153SDan Williams goto out_unlock; 1053006358b3SDave Jiang 1054006358b3SDave Jiang if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) { 1055006358b3SDave Jiang struct nd_cmd_clear_error *clear_err = buf; 1056006358b3SDave Jiang 105723f49844SDan Williams nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address, 1058006358b3SDave Jiang clear_err->cleared); 1059006358b3SDave Jiang } 10600beb2012SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 10610beb2012SDan Williams 106262232e45SDan Williams if (copy_to_user(p, buf, buf_len)) 106362232e45SDan Williams rc = -EFAULT; 10640beb2012SDan Williams 10650beb2012SDan Williams vfree(buf); 10660beb2012SDan Williams return rc; 10670beb2012SDan Williams 1068eaf96153SDan Williams out_unlock: 1069eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 107062232e45SDan Williams out: 107162232e45SDan Williams vfree(buf); 107262232e45SDan Williams return rc; 107362232e45SDan Williams } 107462232e45SDan Williams 107545def22cSDan Williams static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 107645def22cSDan Williams { 107762232e45SDan Williams long id = (long) file->private_data; 10784dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 107962232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 108062232e45SDan Williams 10814dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 108262232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 108362232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 108462232e45SDan Williams if (nvdimm_bus->id == id) { 10854dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, NULL, ro, cmd, arg); 108662232e45SDan Williams break; 108762232e45SDan Williams } 108862232e45SDan Williams } 108962232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 109062232e45SDan Williams 109162232e45SDan Williams return rc; 109262232e45SDan Williams } 109362232e45SDan Williams 109462232e45SDan Williams static int match_dimm(struct device *dev, void *data) 109562232e45SDan Williams { 109662232e45SDan Williams long id = (long) data; 109762232e45SDan Williams 109862232e45SDan Williams if (is_nvdimm(dev)) { 109962232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 110062232e45SDan Williams 110162232e45SDan Williams return nvdimm->id == id; 110262232e45SDan Williams } 110362232e45SDan Williams 110462232e45SDan Williams return 0; 110562232e45SDan Williams } 110662232e45SDan Williams 110762232e45SDan Williams static long nvdimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 110862232e45SDan Williams { 11094dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 111062232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 111162232e45SDan Williams 11124dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 111362232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 111462232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 111562232e45SDan Williams struct device *dev = device_find_child(&nvdimm_bus->dev, 111662232e45SDan Williams file->private_data, match_dimm); 111762232e45SDan Williams struct nvdimm *nvdimm; 111862232e45SDan Williams 111962232e45SDan Williams if (!dev) 112062232e45SDan Williams continue; 112162232e45SDan Williams 112262232e45SDan Williams nvdimm = to_nvdimm(dev); 11234dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg); 112462232e45SDan Williams put_device(dev); 112562232e45SDan Williams break; 112662232e45SDan Williams } 112762232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 112862232e45SDan Williams 112962232e45SDan Williams return rc; 113062232e45SDan Williams } 113162232e45SDan Williams 113262232e45SDan Williams static int nd_open(struct inode *inode, struct file *file) 113362232e45SDan Williams { 113462232e45SDan Williams long minor = iminor(inode); 113562232e45SDan Williams 113662232e45SDan Williams file->private_data = (void *) minor; 113762232e45SDan Williams return 0; 113845def22cSDan Williams } 113945def22cSDan Williams 114045def22cSDan Williams static const struct file_operations nvdimm_bus_fops = { 114145def22cSDan Williams .owner = THIS_MODULE, 114262232e45SDan Williams .open = nd_open, 114345def22cSDan Williams .unlocked_ioctl = nd_ioctl, 114445def22cSDan Williams .compat_ioctl = nd_ioctl, 114545def22cSDan Williams .llseek = noop_llseek, 114645def22cSDan Williams }; 114745def22cSDan Williams 114862232e45SDan Williams static const struct file_operations nvdimm_fops = { 114962232e45SDan Williams .owner = THIS_MODULE, 115062232e45SDan Williams .open = nd_open, 115162232e45SDan Williams .unlocked_ioctl = nvdimm_ioctl, 115262232e45SDan Williams .compat_ioctl = nvdimm_ioctl, 115362232e45SDan Williams .llseek = noop_llseek, 115462232e45SDan Williams }; 115562232e45SDan Williams 115645def22cSDan Williams int __init nvdimm_bus_init(void) 115745def22cSDan Williams { 115845def22cSDan Williams int rc; 115945def22cSDan Williams 1160e6dfb2deSDan Williams rc = bus_register(&nvdimm_bus_type); 1161e6dfb2deSDan Williams if (rc) 1162e6dfb2deSDan Williams return rc; 1163e6dfb2deSDan Williams 116445def22cSDan Williams rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops); 116545def22cSDan Williams if (rc < 0) 116662232e45SDan Williams goto err_bus_chrdev; 116745def22cSDan Williams nvdimm_bus_major = rc; 116845def22cSDan Williams 116962232e45SDan Williams rc = register_chrdev(0, "dimmctl", &nvdimm_fops); 117062232e45SDan Williams if (rc < 0) 117162232e45SDan Williams goto err_dimm_chrdev; 117262232e45SDan Williams nvdimm_major = rc; 117362232e45SDan Williams 117445def22cSDan Williams nd_class = class_create(THIS_MODULE, "nd"); 1175daa1dee4SAxel Lin if (IS_ERR(nd_class)) { 1176daa1dee4SAxel Lin rc = PTR_ERR(nd_class); 117745def22cSDan Williams goto err_class; 1178daa1dee4SAxel Lin } 117945def22cSDan Williams 118018515942SDan Williams rc = driver_register(&nd_bus_driver.drv); 118118515942SDan Williams if (rc) 118218515942SDan Williams goto err_nd_bus; 118318515942SDan Williams 118445def22cSDan Williams return 0; 118545def22cSDan Williams 118618515942SDan Williams err_nd_bus: 118718515942SDan Williams class_destroy(nd_class); 118845def22cSDan Williams err_class: 118962232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 119062232e45SDan Williams err_dimm_chrdev: 119145def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 119262232e45SDan Williams err_bus_chrdev: 1193e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 119445def22cSDan Williams 119545def22cSDan Williams return rc; 119645def22cSDan Williams } 119745def22cSDan Williams 11984d88a97aSDan Williams void nvdimm_bus_exit(void) 119945def22cSDan Williams { 120018515942SDan Williams driver_unregister(&nd_bus_driver.drv); 120145def22cSDan Williams class_destroy(nd_class); 120245def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 120362232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 1204e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 120518515942SDan Williams ida_destroy(&nd_ida); 120645def22cSDan Williams } 1207