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 { 5741d7a6d6SToshi Kani /* 5841d7a6d6SToshi Kani * Ensure that region devices always have their numa node set as 5941d7a6d6SToshi Kani * early as possible. 6041d7a6d6SToshi Kani */ 61c9e582aaSDan Williams if (is_nd_region(dev)) 6241d7a6d6SToshi Kani set_dev_node(dev, to_nd_region(dev)->numa_node); 634d88a97aSDan Williams return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT, 644d88a97aSDan Williams to_nd_device_type(dev)); 654d88a97aSDan Williams } 664d88a97aSDan Williams 673d88002eSDan Williams static struct module *to_bus_provider(struct device *dev) 683d88002eSDan Williams { 693d88002eSDan Williams /* pin bus providers while regions are enabled */ 70c9e582aaSDan Williams if (is_nd_region(dev)) { 713d88002eSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 723d88002eSDan Williams 73bc9775d8SDan Williams return nvdimm_bus->nd_desc->module; 743d88002eSDan Williams } 753d88002eSDan Williams return NULL; 763d88002eSDan Williams } 773d88002eSDan Williams 78eaf96153SDan Williams static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus) 79eaf96153SDan Williams { 80eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 81eaf96153SDan Williams nvdimm_bus->probe_active++; 82eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 83eaf96153SDan Williams } 84eaf96153SDan Williams 85eaf96153SDan Williams static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus) 86eaf96153SDan Williams { 87eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 88eaf96153SDan Williams if (--nvdimm_bus->probe_active == 0) 89eaf96153SDan Williams wake_up(&nvdimm_bus->probe_wait); 90eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 91eaf96153SDan Williams } 92eaf96153SDan Williams 934d88a97aSDan Williams static int nvdimm_bus_probe(struct device *dev) 944d88a97aSDan Williams { 954d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 963d88002eSDan Williams struct module *provider = to_bus_provider(dev); 974d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 984d88a97aSDan Williams int rc; 994d88a97aSDan Williams 1003d88002eSDan Williams if (!try_module_get(provider)) 1013d88002eSDan Williams return -ENXIO; 1023d88002eSDan Williams 103eaf96153SDan Williams nvdimm_bus_probe_start(nvdimm_bus); 1044d88a97aSDan Williams rc = nd_drv->probe(dev); 105eaf96153SDan Williams if (rc == 0) 106eaf96153SDan Williams nd_region_probe_success(nvdimm_bus, dev); 107bf9bccc1SDan Williams else 108bf9bccc1SDan Williams nd_region_disable(nvdimm_bus, dev); 109eaf96153SDan Williams nvdimm_bus_probe_end(nvdimm_bus); 110eaf96153SDan Williams 1114d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name, 1124d88a97aSDan Williams dev_name(dev), rc); 1138c2f7e86SDan Williams 1143d88002eSDan Williams if (rc != 0) 1153d88002eSDan Williams module_put(provider); 1164d88a97aSDan Williams return rc; 1174d88a97aSDan Williams } 1184d88a97aSDan Williams 1194d88a97aSDan Williams static int nvdimm_bus_remove(struct device *dev) 1204d88a97aSDan Williams { 1214d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 1223d88002eSDan Williams struct module *provider = to_bus_provider(dev); 1234d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 1246cf9c5baSDan Williams int rc = 0; 1254d88a97aSDan Williams 1266cf9c5baSDan Williams if (nd_drv->remove) 1274d88a97aSDan Williams rc = nd_drv->remove(dev); 128eaf96153SDan Williams nd_region_disable(nvdimm_bus, dev); 129eaf96153SDan Williams 1304d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name, 1314d88a97aSDan Williams dev_name(dev), rc); 1323d88002eSDan Williams module_put(provider); 1334d88a97aSDan Williams return rc; 1344d88a97aSDan Williams } 1354d88a97aSDan Williams 136476f848aSDan Williams static void nvdimm_bus_shutdown(struct device *dev) 137476f848aSDan Williams { 138476f848aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 139476f848aSDan Williams struct nd_device_driver *nd_drv = NULL; 140476f848aSDan Williams 141476f848aSDan Williams if (dev->driver) 142476f848aSDan Williams nd_drv = to_nd_device_driver(dev->driver); 143476f848aSDan Williams 144476f848aSDan Williams if (nd_drv && nd_drv->shutdown) { 145476f848aSDan Williams nd_drv->shutdown(dev); 146476f848aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.shutdown(%s)\n", 147476f848aSDan Williams dev->driver->name, dev_name(dev)); 148476f848aSDan Williams } 149476f848aSDan Williams } 150476f848aSDan Williams 15171999466SDan Williams void nd_device_notify(struct device *dev, enum nvdimm_event event) 15271999466SDan Williams { 15371999466SDan Williams device_lock(dev); 15471999466SDan Williams if (dev->driver) { 15571999466SDan Williams struct nd_device_driver *nd_drv; 15671999466SDan Williams 15771999466SDan Williams nd_drv = to_nd_device_driver(dev->driver); 15871999466SDan Williams if (nd_drv->notify) 15971999466SDan Williams nd_drv->notify(dev, event); 16071999466SDan Williams } 16171999466SDan Williams device_unlock(dev); 16271999466SDan Williams } 16371999466SDan Williams EXPORT_SYMBOL(nd_device_notify); 16471999466SDan Williams 16571999466SDan Williams void nvdimm_region_notify(struct nd_region *nd_region, enum nvdimm_event event) 16671999466SDan Williams { 16771999466SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 16871999466SDan Williams 16971999466SDan Williams if (!nvdimm_bus) 17071999466SDan Williams return; 17171999466SDan Williams 17271999466SDan Williams /* caller is responsible for holding a reference on the device */ 17371999466SDan Williams nd_device_notify(&nd_region->dev, event); 17471999466SDan Williams } 17571999466SDan Williams EXPORT_SYMBOL_GPL(nvdimm_region_notify); 17671999466SDan Williams 17723f49844SDan Williams struct clear_badblocks_context { 17823f49844SDan Williams resource_size_t phys, cleared; 17923f49844SDan Williams }; 18023f49844SDan Williams 18123f49844SDan Williams static int nvdimm_clear_badblocks_region(struct device *dev, void *data) 18223f49844SDan Williams { 18323f49844SDan Williams struct clear_badblocks_context *ctx = data; 18423f49844SDan Williams struct nd_region *nd_region; 18523f49844SDan Williams resource_size_t ndr_end; 18623f49844SDan Williams sector_t sector; 18723f49844SDan Williams 18823f49844SDan Williams /* make sure device is a region */ 18923f49844SDan Williams if (!is_nd_pmem(dev)) 19023f49844SDan Williams return 0; 19123f49844SDan Williams 19223f49844SDan Williams nd_region = to_nd_region(dev); 19323f49844SDan Williams ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1; 19423f49844SDan Williams 19523f49844SDan Williams /* make sure we are in the region */ 19623f49844SDan Williams if (ctx->phys < nd_region->ndr_start 19723f49844SDan Williams || (ctx->phys + ctx->cleared) > ndr_end) 19823f49844SDan Williams return 0; 19923f49844SDan Williams 20023f49844SDan Williams sector = (ctx->phys - nd_region->ndr_start) / 512; 20123f49844SDan Williams badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512); 20223f49844SDan Williams 203975750a9SToshi Kani if (nd_region->bb_state) 204975750a9SToshi Kani sysfs_notify_dirent(nd_region->bb_state); 205975750a9SToshi Kani 20623f49844SDan Williams return 0; 20723f49844SDan Williams } 20823f49844SDan Williams 20923f49844SDan Williams static void nvdimm_clear_badblocks_regions(struct nvdimm_bus *nvdimm_bus, 21023f49844SDan Williams phys_addr_t phys, u64 cleared) 21123f49844SDan Williams { 21223f49844SDan Williams struct clear_badblocks_context ctx = { 21323f49844SDan Williams .phys = phys, 21423f49844SDan Williams .cleared = cleared, 21523f49844SDan Williams }; 21623f49844SDan Williams 21723f49844SDan Williams device_for_each_child(&nvdimm_bus->dev, &ctx, 21823f49844SDan Williams nvdimm_clear_badblocks_region); 21923f49844SDan Williams } 22023f49844SDan Williams 22123f49844SDan Williams static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus, 22223f49844SDan Williams phys_addr_t phys, u64 cleared) 22323f49844SDan Williams { 22423f49844SDan Williams if (cleared > 0) 225aa9ad44aSDave Jiang badrange_forget(&nvdimm_bus->badrange, phys, cleared); 22623f49844SDan Williams 22723f49844SDan Williams if (cleared > 0 && cleared / 512) 22823f49844SDan Williams nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared); 22923f49844SDan Williams } 23023f49844SDan Williams 23159e64739SDan Williams long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, 23259e64739SDan Williams unsigned int len) 23359e64739SDan Williams { 23459e64739SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 23559e64739SDan Williams struct nvdimm_bus_descriptor *nd_desc; 23659e64739SDan Williams struct nd_cmd_clear_error clear_err; 23759e64739SDan Williams struct nd_cmd_ars_cap ars_cap; 23859e64739SDan Williams u32 clear_err_unit, mask; 2390930a750SVishal Verma unsigned int noio_flag; 24059e64739SDan Williams int cmd_rc, rc; 24159e64739SDan Williams 24259e64739SDan Williams if (!nvdimm_bus) 24359e64739SDan Williams return -ENXIO; 24459e64739SDan Williams 24559e64739SDan Williams nd_desc = nvdimm_bus->nd_desc; 2461e8b8d96SDave Jiang /* 2471e8b8d96SDave Jiang * if ndctl does not exist, it's PMEM_LEGACY and 2481e8b8d96SDave Jiang * we want to just pretend everything is handled. 2491e8b8d96SDave Jiang */ 25059e64739SDan Williams if (!nd_desc->ndctl) 2511e8b8d96SDave Jiang return len; 25259e64739SDan Williams 25359e64739SDan Williams memset(&ars_cap, 0, sizeof(ars_cap)); 25459e64739SDan Williams ars_cap.address = phys; 25559e64739SDan Williams ars_cap.length = len; 2560930a750SVishal Verma noio_flag = memalloc_noio_save(); 25759e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap, 25859e64739SDan Williams sizeof(ars_cap), &cmd_rc); 2590930a750SVishal Verma memalloc_noio_restore(noio_flag); 26059e64739SDan Williams if (rc < 0) 26159e64739SDan Williams return rc; 26259e64739SDan Williams if (cmd_rc < 0) 26359e64739SDan Williams return cmd_rc; 26459e64739SDan Williams clear_err_unit = ars_cap.clear_err_unit; 26559e64739SDan Williams if (!clear_err_unit || !is_power_of_2(clear_err_unit)) 26659e64739SDan Williams return -ENXIO; 26759e64739SDan Williams 26859e64739SDan Williams mask = clear_err_unit - 1; 26959e64739SDan Williams if ((phys | len) & mask) 27059e64739SDan Williams return -ENXIO; 27159e64739SDan Williams memset(&clear_err, 0, sizeof(clear_err)); 27259e64739SDan Williams clear_err.address = phys; 27359e64739SDan Williams clear_err.length = len; 2740930a750SVishal Verma noio_flag = memalloc_noio_save(); 27559e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err, 27659e64739SDan Williams sizeof(clear_err), &cmd_rc); 2770930a750SVishal Verma memalloc_noio_restore(noio_flag); 27859e64739SDan Williams if (rc < 0) 27959e64739SDan Williams return rc; 28059e64739SDan Williams if (cmd_rc < 0) 28159e64739SDan Williams return cmd_rc; 282e046114aSVishal Verma 28323f49844SDan Williams nvdimm_account_cleared_poison(nvdimm_bus, phys, clear_err.cleared); 2848d13c029SToshi Kani 28559e64739SDan Williams return clear_err.cleared; 28659e64739SDan Williams } 28759e64739SDan Williams EXPORT_SYMBOL_GPL(nvdimm_clear_poison); 28859e64739SDan Williams 28918515942SDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv); 29018515942SDan Williams 2914d88a97aSDan Williams static struct bus_type nvdimm_bus_type = { 292e6dfb2deSDan Williams .name = "nd", 2934d88a97aSDan Williams .uevent = nvdimm_bus_uevent, 2944d88a97aSDan Williams .match = nvdimm_bus_match, 2954d88a97aSDan Williams .probe = nvdimm_bus_probe, 2964d88a97aSDan Williams .remove = nvdimm_bus_remove, 297476f848aSDan Williams .shutdown = nvdimm_bus_shutdown, 298e6dfb2deSDan Williams }; 299e6dfb2deSDan Williams 30018515942SDan Williams static void nvdimm_bus_release(struct device *dev) 30118515942SDan Williams { 30218515942SDan Williams struct nvdimm_bus *nvdimm_bus; 30318515942SDan Williams 30418515942SDan Williams nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 30518515942SDan Williams ida_simple_remove(&nd_ida, nvdimm_bus->id); 30618515942SDan Williams kfree(nvdimm_bus); 30718515942SDan Williams } 30818515942SDan Williams 30918515942SDan Williams static bool is_nvdimm_bus(struct device *dev) 31018515942SDan Williams { 31118515942SDan Williams return dev->release == nvdimm_bus_release; 31218515942SDan Williams } 31318515942SDan Williams 31418515942SDan Williams struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) 31518515942SDan Williams { 31618515942SDan Williams struct device *dev; 31718515942SDan Williams 31818515942SDan Williams for (dev = nd_dev; dev; dev = dev->parent) 31918515942SDan Williams if (is_nvdimm_bus(dev)) 32018515942SDan Williams break; 32118515942SDan Williams dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); 32218515942SDan Williams if (dev) 32318515942SDan Williams return to_nvdimm_bus(dev); 32418515942SDan Williams return NULL; 32518515942SDan Williams } 32618515942SDan Williams 32718515942SDan Williams struct nvdimm_bus *to_nvdimm_bus(struct device *dev) 32818515942SDan Williams { 32918515942SDan Williams struct nvdimm_bus *nvdimm_bus; 33018515942SDan Williams 33118515942SDan Williams nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 33218515942SDan Williams WARN_ON(!is_nvdimm_bus(dev)); 33318515942SDan Williams return nvdimm_bus; 33418515942SDan Williams } 33518515942SDan Williams EXPORT_SYMBOL_GPL(to_nvdimm_bus); 33618515942SDan Williams 33718515942SDan Williams struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 33818515942SDan Williams struct nvdimm_bus_descriptor *nd_desc) 33918515942SDan Williams { 34018515942SDan Williams struct nvdimm_bus *nvdimm_bus; 34118515942SDan Williams int rc; 34218515942SDan Williams 34318515942SDan Williams nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); 34418515942SDan Williams if (!nvdimm_bus) 34518515942SDan Williams return NULL; 34618515942SDan Williams INIT_LIST_HEAD(&nvdimm_bus->list); 34718515942SDan Williams INIT_LIST_HEAD(&nvdimm_bus->mapping_list); 34818515942SDan Williams init_waitqueue_head(&nvdimm_bus->probe_wait); 34918515942SDan Williams nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 35018515942SDan Williams mutex_init(&nvdimm_bus->reconfig_mutex); 351aa9ad44aSDave Jiang badrange_init(&nvdimm_bus->badrange); 35218515942SDan Williams if (nvdimm_bus->id < 0) { 35318515942SDan Williams kfree(nvdimm_bus); 35418515942SDan Williams return NULL; 35518515942SDan Williams } 35618515942SDan Williams nvdimm_bus->nd_desc = nd_desc; 35718515942SDan Williams nvdimm_bus->dev.parent = parent; 35818515942SDan Williams nvdimm_bus->dev.release = nvdimm_bus_release; 35918515942SDan Williams nvdimm_bus->dev.groups = nd_desc->attr_groups; 36018515942SDan Williams nvdimm_bus->dev.bus = &nvdimm_bus_type; 3611ff19f48SOliver O'Halloran nvdimm_bus->dev.of_node = nd_desc->of_node; 36218515942SDan Williams dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); 36318515942SDan Williams rc = device_register(&nvdimm_bus->dev); 36418515942SDan Williams if (rc) { 36518515942SDan Williams dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); 36618515942SDan Williams goto err; 36718515942SDan Williams } 36818515942SDan Williams 36918515942SDan Williams return nvdimm_bus; 37018515942SDan Williams err: 37118515942SDan Williams put_device(&nvdimm_bus->dev); 37218515942SDan Williams return NULL; 37318515942SDan Williams } 37418515942SDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_register); 37518515942SDan Williams 37618515942SDan Williams void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) 37718515942SDan Williams { 37818515942SDan Williams if (!nvdimm_bus) 37918515942SDan Williams return; 38018515942SDan Williams device_unregister(&nvdimm_bus->dev); 38118515942SDan Williams } 38218515942SDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); 38318515942SDan Williams 38418515942SDan Williams static int child_unregister(struct device *dev, void *data) 38518515942SDan Williams { 38618515942SDan Williams /* 38718515942SDan Williams * the singular ndctl class device per bus needs to be 38818515942SDan Williams * "device_destroy"ed, so skip it here 38918515942SDan Williams * 39018515942SDan Williams * i.e. remove classless children 39118515942SDan Williams */ 39218515942SDan Williams if (dev->class) 39318515942SDan Williams /* pass */; 39418515942SDan Williams else 39518515942SDan Williams nd_device_unregister(dev, ND_SYNC); 39618515942SDan Williams return 0; 39718515942SDan Williams } 39818515942SDan Williams 399aa9ad44aSDave Jiang static void free_badrange_list(struct list_head *badrange_list) 40018515942SDan Williams { 401aa9ad44aSDave Jiang struct badrange_entry *bre, *next; 40218515942SDan Williams 403aa9ad44aSDave Jiang list_for_each_entry_safe(bre, next, badrange_list, list) { 404aa9ad44aSDave Jiang list_del(&bre->list); 405aa9ad44aSDave Jiang kfree(bre); 40618515942SDan Williams } 407aa9ad44aSDave Jiang list_del_init(badrange_list); 40818515942SDan Williams } 40918515942SDan Williams 41018515942SDan Williams static int nd_bus_remove(struct device *dev) 41118515942SDan Williams { 41218515942SDan Williams struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 41318515942SDan Williams 41418515942SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 41518515942SDan Williams list_del_init(&nvdimm_bus->list); 41618515942SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 41718515942SDan Williams 41818515942SDan Williams nd_synchronize(); 41918515942SDan Williams device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); 42018515942SDan Williams 421aa9ad44aSDave Jiang spin_lock(&nvdimm_bus->badrange.lock); 422aa9ad44aSDave Jiang free_badrange_list(&nvdimm_bus->badrange.list); 423aa9ad44aSDave Jiang spin_unlock(&nvdimm_bus->badrange.lock); 42418515942SDan Williams 42518515942SDan Williams nvdimm_bus_destroy_ndctl(nvdimm_bus); 42618515942SDan Williams 42718515942SDan Williams return 0; 42818515942SDan Williams } 42918515942SDan Williams 43018515942SDan Williams static int nd_bus_probe(struct device *dev) 43118515942SDan Williams { 43218515942SDan Williams struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 43318515942SDan Williams int rc; 43418515942SDan Williams 43518515942SDan Williams rc = nvdimm_bus_create_ndctl(nvdimm_bus); 43618515942SDan Williams if (rc) 43718515942SDan Williams return rc; 43818515942SDan Williams 43918515942SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 44018515942SDan Williams list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list); 44118515942SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 44218515942SDan Williams 44318515942SDan Williams /* enable bus provider attributes to look up their local context */ 44418515942SDan Williams dev_set_drvdata(dev, nvdimm_bus->nd_desc); 44518515942SDan Williams 44618515942SDan Williams return 0; 44718515942SDan Williams } 44818515942SDan Williams 44918515942SDan Williams static struct nd_device_driver nd_bus_driver = { 45018515942SDan Williams .probe = nd_bus_probe, 45118515942SDan Williams .remove = nd_bus_remove, 45218515942SDan Williams .drv = { 45318515942SDan Williams .name = "nd_bus", 45418515942SDan Williams .suppress_bind_attrs = true, 45518515942SDan Williams .bus = &nvdimm_bus_type, 45618515942SDan Williams .owner = THIS_MODULE, 45718515942SDan Williams .mod_name = KBUILD_MODNAME, 45818515942SDan Williams }, 45918515942SDan Williams }; 46018515942SDan Williams 46118515942SDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 46218515942SDan Williams { 46318515942SDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 46418515942SDan Williams 46518515942SDan Williams if (is_nvdimm_bus(dev) && nd_drv == &nd_bus_driver) 46618515942SDan Williams return true; 46718515942SDan Williams 46818515942SDan Williams return !!test_bit(to_nd_device_type(dev), &nd_drv->type); 46918515942SDan Williams } 47018515942SDan Williams 4714d88a97aSDan Williams static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 4724d88a97aSDan Williams 4734d88a97aSDan Williams void nd_synchronize(void) 4744d88a97aSDan Williams { 4754d88a97aSDan Williams async_synchronize_full_domain(&nd_async_domain); 4764d88a97aSDan Williams } 4774d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_synchronize); 4784d88a97aSDan Williams 4794d88a97aSDan Williams static void nd_async_device_register(void *d, async_cookie_t cookie) 4804d88a97aSDan Williams { 4814d88a97aSDan Williams struct device *dev = d; 4824d88a97aSDan Williams 4834d88a97aSDan Williams if (device_add(dev) != 0) { 4844d88a97aSDan Williams dev_err(dev, "%s: failed\n", __func__); 4854d88a97aSDan Williams put_device(dev); 4864d88a97aSDan Williams } 4874d88a97aSDan Williams put_device(dev); 4884d88a97aSDan Williams } 4894d88a97aSDan Williams 4904d88a97aSDan Williams static void nd_async_device_unregister(void *d, async_cookie_t cookie) 4914d88a97aSDan Williams { 4924d88a97aSDan Williams struct device *dev = d; 4934d88a97aSDan Williams 4940ba1c634SDan Williams /* flush bus operations before delete */ 4950ba1c634SDan Williams nvdimm_bus_lock(dev); 4960ba1c634SDan Williams nvdimm_bus_unlock(dev); 4970ba1c634SDan Williams 4984d88a97aSDan Williams device_unregister(dev); 4994d88a97aSDan Williams put_device(dev); 5004d88a97aSDan Williams } 5014d88a97aSDan Williams 5028c2f7e86SDan Williams void __nd_device_register(struct device *dev) 5034d88a97aSDan Williams { 504cd03412aSDan Williams if (!dev) 505cd03412aSDan Williams return; 5064d88a97aSDan Williams dev->bus = &nvdimm_bus_type; 5074d88a97aSDan Williams get_device(dev); 5084d88a97aSDan Williams async_schedule_domain(nd_async_device_register, dev, 5094d88a97aSDan Williams &nd_async_domain); 5104d88a97aSDan Williams } 5118c2f7e86SDan Williams 5128c2f7e86SDan Williams void nd_device_register(struct device *dev) 5138c2f7e86SDan Williams { 5148c2f7e86SDan Williams device_initialize(dev); 5158c2f7e86SDan Williams __nd_device_register(dev); 5168c2f7e86SDan Williams } 5174d88a97aSDan Williams EXPORT_SYMBOL(nd_device_register); 5184d88a97aSDan Williams 5194d88a97aSDan Williams void nd_device_unregister(struct device *dev, enum nd_async_mode mode) 5204d88a97aSDan Williams { 5214d88a97aSDan Williams switch (mode) { 5224d88a97aSDan Williams case ND_ASYNC: 5234d88a97aSDan Williams get_device(dev); 5244d88a97aSDan Williams async_schedule_domain(nd_async_device_unregister, dev, 5254d88a97aSDan Williams &nd_async_domain); 5264d88a97aSDan Williams break; 5274d88a97aSDan Williams case ND_SYNC: 5284d88a97aSDan Williams nd_synchronize(); 5294d88a97aSDan Williams device_unregister(dev); 5304d88a97aSDan Williams break; 5314d88a97aSDan Williams } 5324d88a97aSDan Williams } 5334d88a97aSDan Williams EXPORT_SYMBOL(nd_device_unregister); 5344d88a97aSDan Williams 5354d88a97aSDan Williams /** 5364d88a97aSDan Williams * __nd_driver_register() - register a region or a namespace driver 5374d88a97aSDan Williams * @nd_drv: driver to register 5384d88a97aSDan Williams * @owner: automatically set by nd_driver_register() macro 5394d88a97aSDan Williams * @mod_name: automatically set by nd_driver_register() macro 5404d88a97aSDan Williams */ 5414d88a97aSDan Williams int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner, 5424d88a97aSDan Williams const char *mod_name) 5434d88a97aSDan Williams { 5444d88a97aSDan Williams struct device_driver *drv = &nd_drv->drv; 5454d88a97aSDan Williams 5464d88a97aSDan Williams if (!nd_drv->type) { 5474d88a97aSDan Williams pr_debug("driver type bitmask not set (%pf)\n", 5484d88a97aSDan Williams __builtin_return_address(0)); 5494d88a97aSDan Williams return -EINVAL; 5504d88a97aSDan Williams } 5514d88a97aSDan Williams 5526cf9c5baSDan Williams if (!nd_drv->probe) { 5536cf9c5baSDan Williams pr_debug("%s ->probe() must be specified\n", mod_name); 5544d88a97aSDan Williams return -EINVAL; 5554d88a97aSDan Williams } 5564d88a97aSDan Williams 5574d88a97aSDan Williams drv->bus = &nvdimm_bus_type; 5584d88a97aSDan Williams drv->owner = owner; 5594d88a97aSDan Williams drv->mod_name = mod_name; 5604d88a97aSDan Williams 5614d88a97aSDan Williams return driver_register(drv); 5624d88a97aSDan Williams } 5634d88a97aSDan Williams EXPORT_SYMBOL(__nd_driver_register); 5644d88a97aSDan Williams 56558138820SDan Williams int nvdimm_revalidate_disk(struct gendisk *disk) 56658138820SDan Williams { 56752c44d93SDan Williams struct device *dev = disk_to_dev(disk)->parent; 56858138820SDan Williams struct nd_region *nd_region = to_nd_region(dev->parent); 569254a4cd5SRobert Elliott int disk_ro = get_disk_ro(disk); 57058138820SDan Williams 571254a4cd5SRobert Elliott /* 572254a4cd5SRobert Elliott * Upgrade to read-only if the region is read-only preserve as 573254a4cd5SRobert Elliott * read-only if the disk is already read-only. 574254a4cd5SRobert Elliott */ 575254a4cd5SRobert Elliott if (disk_ro || nd_region->ro == disk_ro) 57658138820SDan Williams return 0; 57758138820SDan Williams 578254a4cd5SRobert Elliott dev_info(dev, "%s read-only, marking %s read-only\n", 579254a4cd5SRobert Elliott dev_name(&nd_region->dev), disk->disk_name); 580254a4cd5SRobert Elliott set_disk_ro(disk, 1); 58158138820SDan Williams 58258138820SDan Williams return 0; 58358138820SDan Williams 58458138820SDan Williams } 58558138820SDan Williams EXPORT_SYMBOL(nvdimm_revalidate_disk); 58658138820SDan Williams 5874d88a97aSDan Williams static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 5884d88a97aSDan Williams char *buf) 5894d88a97aSDan Williams { 5904d88a97aSDan Williams return sprintf(buf, ND_DEVICE_MODALIAS_FMT "\n", 5914d88a97aSDan Williams to_nd_device_type(dev)); 5924d88a97aSDan Williams } 5934d88a97aSDan Williams static DEVICE_ATTR_RO(modalias); 5944d88a97aSDan Williams 5954d88a97aSDan Williams static ssize_t devtype_show(struct device *dev, struct device_attribute *attr, 5964d88a97aSDan Williams char *buf) 5974d88a97aSDan Williams { 5984d88a97aSDan Williams return sprintf(buf, "%s\n", dev->type->name); 5994d88a97aSDan Williams } 6004d88a97aSDan Williams static DEVICE_ATTR_RO(devtype); 6014d88a97aSDan Williams 6024d88a97aSDan Williams static struct attribute *nd_device_attributes[] = { 6034d88a97aSDan Williams &dev_attr_modalias.attr, 6044d88a97aSDan Williams &dev_attr_devtype.attr, 6054d88a97aSDan Williams NULL, 6064d88a97aSDan Williams }; 6074d88a97aSDan Williams 6084d88a97aSDan Williams /** 6094d88a97aSDan Williams * nd_device_attribute_group - generic attributes for all devices on an nd bus 6104d88a97aSDan Williams */ 6114d88a97aSDan Williams struct attribute_group nd_device_attribute_group = { 6124d88a97aSDan Williams .attrs = nd_device_attributes, 6134d88a97aSDan Williams }; 6144d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_device_attribute_group); 6154d88a97aSDan Williams 61674ae66c3SToshi Kani static ssize_t numa_node_show(struct device *dev, 61774ae66c3SToshi Kani struct device_attribute *attr, char *buf) 61874ae66c3SToshi Kani { 61974ae66c3SToshi Kani return sprintf(buf, "%d\n", dev_to_node(dev)); 62074ae66c3SToshi Kani } 62174ae66c3SToshi Kani static DEVICE_ATTR_RO(numa_node); 62274ae66c3SToshi Kani 62374ae66c3SToshi Kani static struct attribute *nd_numa_attributes[] = { 62474ae66c3SToshi Kani &dev_attr_numa_node.attr, 62574ae66c3SToshi Kani NULL, 62674ae66c3SToshi Kani }; 62774ae66c3SToshi Kani 62874ae66c3SToshi Kani static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a, 62974ae66c3SToshi Kani int n) 63074ae66c3SToshi Kani { 63174ae66c3SToshi Kani if (!IS_ENABLED(CONFIG_NUMA)) 63274ae66c3SToshi Kani return 0; 63374ae66c3SToshi Kani 63474ae66c3SToshi Kani return a->mode; 63574ae66c3SToshi Kani } 63674ae66c3SToshi Kani 63774ae66c3SToshi Kani /** 63874ae66c3SToshi Kani * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus 63974ae66c3SToshi Kani */ 64074ae66c3SToshi Kani struct attribute_group nd_numa_attribute_group = { 64174ae66c3SToshi Kani .attrs = nd_numa_attributes, 64274ae66c3SToshi Kani .is_visible = nd_numa_attr_visible, 64374ae66c3SToshi Kani }; 64474ae66c3SToshi Kani EXPORT_SYMBOL_GPL(nd_numa_attribute_group); 64574ae66c3SToshi Kani 64645def22cSDan Williams int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) 64745def22cSDan Williams { 64845def22cSDan Williams dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id); 64945def22cSDan Williams struct device *dev; 65045def22cSDan Williams 65145def22cSDan Williams dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus, 65245def22cSDan Williams "ndctl%d", nvdimm_bus->id); 65345def22cSDan Williams 65442588958SDan Williams if (IS_ERR(dev)) 65545def22cSDan Williams dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld\n", 65645def22cSDan Williams nvdimm_bus->id, PTR_ERR(dev)); 65742588958SDan Williams return PTR_ERR_OR_ZERO(dev); 65845def22cSDan Williams } 65945def22cSDan Williams 66045def22cSDan Williams void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus) 66145def22cSDan Williams { 66245def22cSDan Williams device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id)); 66345def22cSDan Williams } 66445def22cSDan Williams 66562232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = { 66662232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 66762232e45SDan Williams [ND_CMD_SMART] = { 66862232e45SDan Williams .out_num = 2, 66921129112SDan Williams .out_sizes = { 4, 128, }, 67062232e45SDan Williams }, 67162232e45SDan Williams [ND_CMD_SMART_THRESHOLD] = { 67262232e45SDan Williams .out_num = 2, 67362232e45SDan Williams .out_sizes = { 4, 8, }, 67462232e45SDan Williams }, 67562232e45SDan Williams [ND_CMD_DIMM_FLAGS] = { 67662232e45SDan Williams .out_num = 2, 67762232e45SDan Williams .out_sizes = { 4, 4 }, 67862232e45SDan Williams }, 67962232e45SDan Williams [ND_CMD_GET_CONFIG_SIZE] = { 68062232e45SDan Williams .out_num = 3, 68162232e45SDan Williams .out_sizes = { 4, 4, 4, }, 68262232e45SDan Williams }, 68362232e45SDan Williams [ND_CMD_GET_CONFIG_DATA] = { 68462232e45SDan Williams .in_num = 2, 68562232e45SDan Williams .in_sizes = { 4, 4, }, 68662232e45SDan Williams .out_num = 2, 68762232e45SDan Williams .out_sizes = { 4, UINT_MAX, }, 68862232e45SDan Williams }, 68962232e45SDan Williams [ND_CMD_SET_CONFIG_DATA] = { 69062232e45SDan Williams .in_num = 3, 69162232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 69262232e45SDan Williams .out_num = 1, 69362232e45SDan Williams .out_sizes = { 4, }, 69462232e45SDan Williams }, 69562232e45SDan Williams [ND_CMD_VENDOR] = { 69662232e45SDan Williams .in_num = 3, 69762232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 69862232e45SDan Williams .out_num = 3, 69962232e45SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 70062232e45SDan Williams }, 70131eca76bSDan Williams [ND_CMD_CALL] = { 70231eca76bSDan Williams .in_num = 2, 70331eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 70431eca76bSDan Williams .out_num = 1, 70531eca76bSDan Williams .out_sizes = { UINT_MAX, }, 70631eca76bSDan Williams }, 70762232e45SDan Williams }; 70862232e45SDan Williams 70962232e45SDan Williams const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) 71062232e45SDan Williams { 71162232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_dimm_descs)) 71262232e45SDan Williams return &__nd_cmd_dimm_descs[cmd]; 71362232e45SDan Williams return NULL; 71462232e45SDan Williams } 71562232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_dimm_desc); 71662232e45SDan Williams 71762232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { 71862232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 71962232e45SDan Williams [ND_CMD_ARS_CAP] = { 72062232e45SDan Williams .in_num = 2, 72162232e45SDan Williams .in_sizes = { 8, 8, }, 7224577b066SDan Williams .out_num = 4, 7234577b066SDan Williams .out_sizes = { 4, 4, 4, 4, }, 72462232e45SDan Williams }, 72562232e45SDan Williams [ND_CMD_ARS_START] = { 7264577b066SDan Williams .in_num = 5, 7274577b066SDan Williams .in_sizes = { 8, 8, 2, 1, 5, }, 7284577b066SDan Williams .out_num = 2, 7294577b066SDan Williams .out_sizes = { 4, 4, }, 73062232e45SDan Williams }, 73162232e45SDan Williams [ND_CMD_ARS_STATUS] = { 732747ffe11SDan Williams .out_num = 3, 733747ffe11SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 73462232e45SDan Williams }, 735d4f32367SDan Williams [ND_CMD_CLEAR_ERROR] = { 736d4f32367SDan Williams .in_num = 2, 737d4f32367SDan Williams .in_sizes = { 8, 8, }, 738d4f32367SDan Williams .out_num = 3, 739d4f32367SDan Williams .out_sizes = { 4, 4, 8, }, 740d4f32367SDan Williams }, 74131eca76bSDan Williams [ND_CMD_CALL] = { 74231eca76bSDan Williams .in_num = 2, 74331eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 74431eca76bSDan Williams .out_num = 1, 74531eca76bSDan Williams .out_sizes = { UINT_MAX, }, 74631eca76bSDan Williams }, 74762232e45SDan Williams }; 74862232e45SDan Williams 74962232e45SDan Williams const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) 75062232e45SDan Williams { 75162232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_bus_descs)) 75262232e45SDan Williams return &__nd_cmd_bus_descs[cmd]; 75362232e45SDan Williams return NULL; 75462232e45SDan Williams } 75562232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_bus_desc); 75662232e45SDan Williams 75762232e45SDan Williams u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, 75862232e45SDan Williams const struct nd_cmd_desc *desc, int idx, void *buf) 75962232e45SDan Williams { 76062232e45SDan Williams if (idx >= desc->in_num) 76162232e45SDan Williams return UINT_MAX; 76262232e45SDan Williams 76362232e45SDan Williams if (desc->in_sizes[idx] < UINT_MAX) 76462232e45SDan Williams return desc->in_sizes[idx]; 76562232e45SDan Williams 76662232e45SDan Williams if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA && idx == 2) { 76762232e45SDan Williams struct nd_cmd_set_config_hdr *hdr = buf; 76862232e45SDan Williams 76962232e45SDan Williams return hdr->in_length; 77062232e45SDan Williams } else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) { 77162232e45SDan Williams struct nd_cmd_vendor_hdr *hdr = buf; 77262232e45SDan Williams 77362232e45SDan Williams return hdr->in_length; 77431eca76bSDan Williams } else if (cmd == ND_CMD_CALL) { 77531eca76bSDan Williams struct nd_cmd_pkg *pkg = buf; 77631eca76bSDan Williams 77731eca76bSDan Williams return pkg->nd_size_in; 77862232e45SDan Williams } 77962232e45SDan Williams 78062232e45SDan Williams return UINT_MAX; 78162232e45SDan Williams } 78262232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_in_size); 78362232e45SDan Williams 78462232e45SDan Williams u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd, 78562232e45SDan Williams const struct nd_cmd_desc *desc, int idx, const u32 *in_field, 786efda1b5dSDan Williams const u32 *out_field, unsigned long remainder) 78762232e45SDan Williams { 78862232e45SDan Williams if (idx >= desc->out_num) 78962232e45SDan Williams return UINT_MAX; 79062232e45SDan Williams 79162232e45SDan Williams if (desc->out_sizes[idx] < UINT_MAX) 79262232e45SDan Williams return desc->out_sizes[idx]; 79362232e45SDan Williams 79462232e45SDan Williams if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && idx == 1) 79562232e45SDan Williams return in_field[1]; 79662232e45SDan Williams else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) 79762232e45SDan Williams return out_field[1]; 798efda1b5dSDan Williams else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) { 799efda1b5dSDan Williams /* 800efda1b5dSDan Williams * Per table 9-276 ARS Data in ACPI 6.1, out_field[1] is 801efda1b5dSDan Williams * "Size of Output Buffer in bytes, including this 802efda1b5dSDan Williams * field." 803efda1b5dSDan Williams */ 804efda1b5dSDan Williams if (out_field[1] < 4) 805efda1b5dSDan Williams return 0; 806efda1b5dSDan Williams /* 807efda1b5dSDan Williams * ACPI 6.1 is ambiguous if 'status' is included in the 808efda1b5dSDan Williams * output size. If we encounter an output size that 809efda1b5dSDan Williams * overshoots the remainder by 4 bytes, assume it was 810efda1b5dSDan Williams * including 'status'. 811efda1b5dSDan Williams */ 812efda1b5dSDan Williams if (out_field[1] - 8 == remainder) 813efda1b5dSDan Williams return remainder; 814efda1b5dSDan Williams return out_field[1] - 4; 815efda1b5dSDan Williams } else if (cmd == ND_CMD_CALL) { 81631eca76bSDan Williams struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field; 81731eca76bSDan Williams 81831eca76bSDan Williams return pkg->nd_size_out; 81931eca76bSDan Williams } 82031eca76bSDan Williams 82162232e45SDan Williams 82262232e45SDan Williams return UINT_MAX; 82362232e45SDan Williams } 82462232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_out_size); 82562232e45SDan Williams 826bf9bccc1SDan Williams void wait_nvdimm_bus_probe_idle(struct device *dev) 827eaf96153SDan Williams { 828bf9bccc1SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 829bf9bccc1SDan Williams 830eaf96153SDan Williams do { 831eaf96153SDan Williams if (nvdimm_bus->probe_active == 0) 832eaf96153SDan Williams break; 833eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 834eaf96153SDan Williams wait_event(nvdimm_bus->probe_wait, 835eaf96153SDan Williams nvdimm_bus->probe_active == 0); 836eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 837eaf96153SDan Williams } while (true); 838eaf96153SDan Williams } 839eaf96153SDan Williams 840006358b3SDave Jiang static int nd_pmem_forget_poison_check(struct device *dev, void *data) 841d4f32367SDan Williams { 842006358b3SDave Jiang struct nd_cmd_clear_error *clear_err = 843006358b3SDave Jiang (struct nd_cmd_clear_error *)data; 844006358b3SDave Jiang struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; 845006358b3SDave Jiang struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL; 846006358b3SDave Jiang struct nd_dax *nd_dax = is_nd_dax(dev) ? to_nd_dax(dev) : NULL; 847006358b3SDave Jiang struct nd_namespace_common *ndns = NULL; 848006358b3SDave Jiang struct nd_namespace_io *nsio; 849006358b3SDave Jiang resource_size_t offset = 0, end_trunc = 0, start, end, pstart, pend; 850006358b3SDave Jiang 851006358b3SDave Jiang if (nd_dax || !dev->driver) 852d4f32367SDan Williams return 0; 853006358b3SDave Jiang 854006358b3SDave Jiang start = clear_err->address; 855006358b3SDave Jiang end = clear_err->address + clear_err->cleared - 1; 856006358b3SDave Jiang 857006358b3SDave Jiang if (nd_btt || nd_pfn || nd_dax) { 858006358b3SDave Jiang if (nd_btt) 859006358b3SDave Jiang ndns = nd_btt->ndns; 860006358b3SDave Jiang else if (nd_pfn) 861006358b3SDave Jiang ndns = nd_pfn->ndns; 862006358b3SDave Jiang else if (nd_dax) 863006358b3SDave Jiang ndns = nd_dax->nd_pfn.ndns; 864006358b3SDave Jiang 865006358b3SDave Jiang if (!ndns) 866006358b3SDave Jiang return 0; 867006358b3SDave Jiang } else 868006358b3SDave Jiang ndns = to_ndns(dev); 869006358b3SDave Jiang 870006358b3SDave Jiang nsio = to_nd_namespace_io(&ndns->dev); 871006358b3SDave Jiang pstart = nsio->res.start + offset; 872006358b3SDave Jiang pend = nsio->res.end - end_trunc; 873006358b3SDave Jiang 874006358b3SDave Jiang if ((pstart >= start) && (pend <= end)) 875006358b3SDave Jiang return -EBUSY; 876006358b3SDave Jiang 877006358b3SDave Jiang return 0; 878006358b3SDave Jiang 879006358b3SDave Jiang } 880006358b3SDave Jiang 881006358b3SDave Jiang static int nd_ns_forget_poison_check(struct device *dev, void *data) 882006358b3SDave Jiang { 883006358b3SDave Jiang return device_for_each_child(dev, data, nd_pmem_forget_poison_check); 884d4f32367SDan Williams } 885d4f32367SDan Williams 886eaf96153SDan Williams /* set_config requires an idle interleave set */ 88787bf572eSDan Williams static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, 888006358b3SDave Jiang struct nvdimm *nvdimm, unsigned int cmd, void *data) 889eaf96153SDan Williams { 89087bf572eSDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 89187bf572eSDan Williams 89287bf572eSDan Williams /* ask the bus provider if it would like to block this request */ 89387bf572eSDan Williams if (nd_desc->clear_to_send) { 89487bf572eSDan Williams int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd); 89587bf572eSDan Williams 89687bf572eSDan Williams if (rc) 89787bf572eSDan Williams return rc; 89887bf572eSDan Williams } 899eaf96153SDan Williams 900d4f32367SDan Williams /* require clear error to go through the pmem driver */ 901d4f32367SDan Williams if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR) 902006358b3SDave Jiang return device_for_each_child(&nvdimm_bus->dev, data, 903006358b3SDave Jiang nd_ns_forget_poison_check); 904d4f32367SDan Williams 905eaf96153SDan Williams if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) 906eaf96153SDan Williams return 0; 907eaf96153SDan Williams 90887bf572eSDan Williams /* prevent label manipulation while the kernel owns label updates */ 909bf9bccc1SDan Williams wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev); 910eaf96153SDan Williams if (atomic_read(&nvdimm->busy)) 911eaf96153SDan Williams return -EBUSY; 912eaf96153SDan Williams return 0; 913eaf96153SDan Williams } 914eaf96153SDan Williams 91562232e45SDan Williams static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 91662232e45SDan Williams int read_only, unsigned int ioctl_cmd, unsigned long arg) 91762232e45SDan Williams { 91862232e45SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 91962232e45SDan Williams static char out_env[ND_CMD_MAX_ENVELOPE]; 92062232e45SDan Williams static char in_env[ND_CMD_MAX_ENVELOPE]; 92162232e45SDan Williams const struct nd_cmd_desc *desc = NULL; 92262232e45SDan Williams unsigned int cmd = _IOC_NR(ioctl_cmd); 92362232e45SDan Williams struct device *dev = &nvdimm_bus->dev; 92458738c49SDan Williams void __user *p = (void __user *) arg; 92562232e45SDan Williams const char *cmd_name, *dimm_name; 92658738c49SDan Williams u32 in_len = 0, out_len = 0; 92758738c49SDan Williams unsigned int func = cmd; 928e3654ecaSDan Williams unsigned long cmd_mask; 92958738c49SDan Williams struct nd_cmd_pkg pkg; 930006358b3SDave Jiang int rc, i, cmd_rc; 93158738c49SDan Williams u64 buf_len = 0; 93258738c49SDan Williams void *buf; 93362232e45SDan Williams 93462232e45SDan Williams if (nvdimm) { 93562232e45SDan Williams desc = nd_cmd_dimm_desc(cmd); 93662232e45SDan Williams cmd_name = nvdimm_cmd_name(cmd); 937e3654ecaSDan Williams cmd_mask = nvdimm->cmd_mask; 93862232e45SDan Williams dimm_name = dev_name(&nvdimm->dev); 93962232e45SDan Williams } else { 94062232e45SDan Williams desc = nd_cmd_bus_desc(cmd); 94162232e45SDan Williams cmd_name = nvdimm_bus_cmd_name(cmd); 942e3654ecaSDan Williams cmd_mask = nd_desc->cmd_mask; 94362232e45SDan Williams dimm_name = "bus"; 94462232e45SDan Williams } 94562232e45SDan Williams 94631eca76bSDan Williams if (cmd == ND_CMD_CALL) { 94731eca76bSDan Williams if (copy_from_user(&pkg, p, sizeof(pkg))) 94831eca76bSDan Williams return -EFAULT; 94931eca76bSDan Williams } 95031eca76bSDan Williams 95162232e45SDan Williams if (!desc || (desc->out_num + desc->in_num == 0) || 952e3654ecaSDan Williams !test_bit(cmd, &cmd_mask)) 95362232e45SDan Williams return -ENOTTY; 95462232e45SDan Williams 95562232e45SDan Williams /* fail write commands (when read-only) */ 95662232e45SDan Williams if (read_only) 95707accfa9SJerry Hoemann switch (cmd) { 95807accfa9SJerry Hoemann case ND_CMD_VENDOR: 95907accfa9SJerry Hoemann case ND_CMD_SET_CONFIG_DATA: 96007accfa9SJerry Hoemann case ND_CMD_ARS_START: 961d4f32367SDan Williams case ND_CMD_CLEAR_ERROR: 96231eca76bSDan Williams case ND_CMD_CALL: 96362232e45SDan Williams dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 96462232e45SDan Williams nvdimm ? nvdimm_cmd_name(cmd) 96562232e45SDan Williams : nvdimm_bus_cmd_name(cmd)); 96662232e45SDan Williams return -EPERM; 96762232e45SDan Williams default: 96862232e45SDan Williams break; 96962232e45SDan Williams } 97062232e45SDan Williams 97162232e45SDan Williams /* process an input envelope */ 97262232e45SDan Williams for (i = 0; i < desc->in_num; i++) { 97362232e45SDan Williams u32 in_size, copy; 97462232e45SDan Williams 97562232e45SDan Williams in_size = nd_cmd_in_size(nvdimm, cmd, desc, i, in_env); 97662232e45SDan Williams if (in_size == UINT_MAX) { 97762232e45SDan Williams dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n", 97862232e45SDan Williams __func__, dimm_name, cmd_name, i); 97962232e45SDan Williams return -ENXIO; 98062232e45SDan Williams } 98162232e45SDan Williams if (in_len < sizeof(in_env)) 98262232e45SDan Williams copy = min_t(u32, sizeof(in_env) - in_len, in_size); 98362232e45SDan Williams else 98462232e45SDan Williams copy = 0; 98562232e45SDan Williams if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) 98662232e45SDan Williams return -EFAULT; 98762232e45SDan Williams in_len += in_size; 98862232e45SDan Williams } 98962232e45SDan Williams 99031eca76bSDan Williams if (cmd == ND_CMD_CALL) { 99153b85a44SJerry Hoemann func = pkg.nd_command; 992426824d6SDan Williams dev_dbg(dev, "%s, idx: %llu, in: %u, out: %u, len %llu\n", 993426824d6SDan Williams dimm_name, pkg.nd_command, 99431eca76bSDan Williams in_len, out_len, buf_len); 99531eca76bSDan Williams } 99631eca76bSDan Williams 99762232e45SDan Williams /* process an output envelope */ 99862232e45SDan Williams for (i = 0; i < desc->out_num; i++) { 99962232e45SDan Williams u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, 1000efda1b5dSDan Williams (u32 *) in_env, (u32 *) out_env, 0); 100162232e45SDan Williams u32 copy; 100262232e45SDan Williams 100362232e45SDan Williams if (out_size == UINT_MAX) { 1004426824d6SDan Williams dev_dbg(dev, "%s unknown output size cmd: %s field: %d\n", 1005426824d6SDan Williams dimm_name, cmd_name, i); 100662232e45SDan Williams return -EFAULT; 100762232e45SDan Williams } 100862232e45SDan Williams if (out_len < sizeof(out_env)) 100962232e45SDan Williams copy = min_t(u32, sizeof(out_env) - out_len, out_size); 101062232e45SDan Williams else 101162232e45SDan Williams copy = 0; 101262232e45SDan Williams if (copy && copy_from_user(&out_env[out_len], 101362232e45SDan Williams p + in_len + out_len, copy)) 101462232e45SDan Williams return -EFAULT; 101562232e45SDan Williams out_len += out_size; 101662232e45SDan Williams } 101762232e45SDan Williams 101858738c49SDan Williams buf_len = (u64) out_len + (u64) in_len; 101962232e45SDan Williams if (buf_len > ND_IOCTL_MAX_BUFLEN) { 1020426824d6SDan Williams dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d\n", dimm_name, 1021426824d6SDan Williams cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN); 102262232e45SDan Williams return -EINVAL; 102362232e45SDan Williams } 102462232e45SDan Williams 102562232e45SDan Williams buf = vmalloc(buf_len); 102662232e45SDan Williams if (!buf) 102762232e45SDan Williams return -ENOMEM; 102862232e45SDan Williams 102962232e45SDan Williams if (copy_from_user(buf, p, buf_len)) { 103062232e45SDan Williams rc = -EFAULT; 103162232e45SDan Williams goto out; 103262232e45SDan Williams } 103362232e45SDan Williams 1034eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 103553b85a44SJerry Hoemann rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf); 1036eaf96153SDan Williams if (rc) 1037eaf96153SDan Williams goto out_unlock; 1038eaf96153SDan Williams 1039006358b3SDave Jiang rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc); 104062232e45SDan Williams if (rc < 0) 1041eaf96153SDan Williams goto out_unlock; 1042006358b3SDave Jiang 1043006358b3SDave Jiang if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) { 1044006358b3SDave Jiang struct nd_cmd_clear_error *clear_err = buf; 1045006358b3SDave Jiang 104623f49844SDan Williams nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address, 1047006358b3SDave Jiang clear_err->cleared); 1048006358b3SDave Jiang } 10490beb2012SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 10500beb2012SDan Williams 105162232e45SDan Williams if (copy_to_user(p, buf, buf_len)) 105262232e45SDan Williams rc = -EFAULT; 10530beb2012SDan Williams 10540beb2012SDan Williams vfree(buf); 10550beb2012SDan Williams return rc; 10560beb2012SDan Williams 1057eaf96153SDan Williams out_unlock: 1058eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 105962232e45SDan Williams out: 106062232e45SDan Williams vfree(buf); 106162232e45SDan Williams return rc; 106262232e45SDan Williams } 106362232e45SDan Williams 106445def22cSDan Williams static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 106545def22cSDan Williams { 106662232e45SDan Williams long id = (long) file->private_data; 10674dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 106862232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 106962232e45SDan Williams 10704dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 107162232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 107262232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 107362232e45SDan Williams if (nvdimm_bus->id == id) { 10744dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, NULL, ro, cmd, arg); 107562232e45SDan Williams break; 107662232e45SDan Williams } 107762232e45SDan Williams } 107862232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 107962232e45SDan Williams 108062232e45SDan Williams return rc; 108162232e45SDan Williams } 108262232e45SDan Williams 108362232e45SDan Williams static int match_dimm(struct device *dev, void *data) 108462232e45SDan Williams { 108562232e45SDan Williams long id = (long) data; 108662232e45SDan Williams 108762232e45SDan Williams if (is_nvdimm(dev)) { 108862232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 108962232e45SDan Williams 109062232e45SDan Williams return nvdimm->id == id; 109162232e45SDan Williams } 109262232e45SDan Williams 109362232e45SDan Williams return 0; 109462232e45SDan Williams } 109562232e45SDan Williams 109662232e45SDan Williams static long nvdimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 109762232e45SDan Williams { 10984dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 109962232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 110062232e45SDan Williams 11014dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 110262232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 110362232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 110462232e45SDan Williams struct device *dev = device_find_child(&nvdimm_bus->dev, 110562232e45SDan Williams file->private_data, match_dimm); 110662232e45SDan Williams struct nvdimm *nvdimm; 110762232e45SDan Williams 110862232e45SDan Williams if (!dev) 110962232e45SDan Williams continue; 111062232e45SDan Williams 111162232e45SDan Williams nvdimm = to_nvdimm(dev); 11124dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg); 111362232e45SDan Williams put_device(dev); 111462232e45SDan Williams break; 111562232e45SDan Williams } 111662232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 111762232e45SDan Williams 111862232e45SDan Williams return rc; 111962232e45SDan Williams } 112062232e45SDan Williams 112162232e45SDan Williams static int nd_open(struct inode *inode, struct file *file) 112262232e45SDan Williams { 112362232e45SDan Williams long minor = iminor(inode); 112462232e45SDan Williams 112562232e45SDan Williams file->private_data = (void *) minor; 112662232e45SDan Williams return 0; 112745def22cSDan Williams } 112845def22cSDan Williams 112945def22cSDan Williams static const struct file_operations nvdimm_bus_fops = { 113045def22cSDan Williams .owner = THIS_MODULE, 113162232e45SDan Williams .open = nd_open, 113245def22cSDan Williams .unlocked_ioctl = nd_ioctl, 113345def22cSDan Williams .compat_ioctl = nd_ioctl, 113445def22cSDan Williams .llseek = noop_llseek, 113545def22cSDan Williams }; 113645def22cSDan Williams 113762232e45SDan Williams static const struct file_operations nvdimm_fops = { 113862232e45SDan Williams .owner = THIS_MODULE, 113962232e45SDan Williams .open = nd_open, 114062232e45SDan Williams .unlocked_ioctl = nvdimm_ioctl, 114162232e45SDan Williams .compat_ioctl = nvdimm_ioctl, 114262232e45SDan Williams .llseek = noop_llseek, 114362232e45SDan Williams }; 114462232e45SDan Williams 114545def22cSDan Williams int __init nvdimm_bus_init(void) 114645def22cSDan Williams { 114745def22cSDan Williams int rc; 114845def22cSDan Williams 1149e6dfb2deSDan Williams rc = bus_register(&nvdimm_bus_type); 1150e6dfb2deSDan Williams if (rc) 1151e6dfb2deSDan Williams return rc; 1152e6dfb2deSDan Williams 115345def22cSDan Williams rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops); 115445def22cSDan Williams if (rc < 0) 115562232e45SDan Williams goto err_bus_chrdev; 115645def22cSDan Williams nvdimm_bus_major = rc; 115745def22cSDan Williams 115862232e45SDan Williams rc = register_chrdev(0, "dimmctl", &nvdimm_fops); 115962232e45SDan Williams if (rc < 0) 116062232e45SDan Williams goto err_dimm_chrdev; 116162232e45SDan Williams nvdimm_major = rc; 116262232e45SDan Williams 116345def22cSDan Williams nd_class = class_create(THIS_MODULE, "nd"); 1164daa1dee4SAxel Lin if (IS_ERR(nd_class)) { 1165daa1dee4SAxel Lin rc = PTR_ERR(nd_class); 116645def22cSDan Williams goto err_class; 1167daa1dee4SAxel Lin } 116845def22cSDan Williams 116918515942SDan Williams rc = driver_register(&nd_bus_driver.drv); 117018515942SDan Williams if (rc) 117118515942SDan Williams goto err_nd_bus; 117218515942SDan Williams 117345def22cSDan Williams return 0; 117445def22cSDan Williams 117518515942SDan Williams err_nd_bus: 117618515942SDan Williams class_destroy(nd_class); 117745def22cSDan Williams err_class: 117862232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 117962232e45SDan Williams err_dimm_chrdev: 118045def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 118162232e45SDan Williams err_bus_chrdev: 1182e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 118345def22cSDan Williams 118445def22cSDan Williams return rc; 118545def22cSDan Williams } 118645def22cSDan Williams 11874d88a97aSDan Williams void nvdimm_bus_exit(void) 118845def22cSDan Williams { 118918515942SDan Williams driver_unregister(&nd_bus_driver.drv); 119045def22cSDan Williams class_destroy(nd_class); 119145def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 119262232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 1193e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 119418515942SDan Williams ida_destroy(&nd_ida); 119545def22cSDan Williams } 1196