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 1462232e45SDan Williams #include <linux/vmalloc.h> 1545def22cSDan Williams #include <linux/uaccess.h> 163d88002eSDan Williams #include <linux/module.h> 1745def22cSDan Williams #include <linux/fcntl.h> 18e6dfb2deSDan Williams #include <linux/async.h> 1962232e45SDan Williams #include <linux/ndctl.h> 204d88a97aSDan Williams #include <linux/sched.h> 2145def22cSDan Williams #include <linux/slab.h> 2245def22cSDan Williams #include <linux/fs.h> 2345def22cSDan Williams #include <linux/io.h> 2462232e45SDan Williams #include <linux/mm.h> 254d88a97aSDan Williams #include <linux/nd.h> 2645def22cSDan Williams #include "nd-core.h" 274d88a97aSDan Williams #include "nd.h" 2845def22cSDan Williams 2962232e45SDan Williams int nvdimm_major; 3045def22cSDan Williams static int nvdimm_bus_major; 3145def22cSDan Williams static struct class *nd_class; 3245def22cSDan Williams 334d88a97aSDan Williams static int to_nd_device_type(struct device *dev) 344d88a97aSDan Williams { 354d88a97aSDan Williams if (is_nvdimm(dev)) 364d88a97aSDan Williams return ND_DEVICE_DIMM; 373d88002eSDan Williams else if (is_nd_pmem(dev)) 383d88002eSDan Williams return ND_DEVICE_REGION_PMEM; 393d88002eSDan Williams else if (is_nd_blk(dev)) 403d88002eSDan Williams return ND_DEVICE_REGION_BLK; 413d88002eSDan Williams else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent)) 423d88002eSDan Williams return nd_region_to_nstype(to_nd_region(dev->parent)); 434d88a97aSDan Williams 444d88a97aSDan Williams return 0; 454d88a97aSDan Williams } 464d88a97aSDan Williams 474d88a97aSDan Williams static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env) 484d88a97aSDan Williams { 494d88a97aSDan Williams return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT, 504d88a97aSDan Williams to_nd_device_type(dev)); 514d88a97aSDan Williams } 524d88a97aSDan Williams 534d88a97aSDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 544d88a97aSDan Williams { 554d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 564d88a97aSDan Williams 574d88a97aSDan Williams return test_bit(to_nd_device_type(dev), &nd_drv->type); 584d88a97aSDan Williams } 594d88a97aSDan Williams 603d88002eSDan Williams static struct module *to_bus_provider(struct device *dev) 613d88002eSDan Williams { 623d88002eSDan Williams /* pin bus providers while regions are enabled */ 633d88002eSDan Williams if (is_nd_pmem(dev) || is_nd_blk(dev)) { 643d88002eSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 653d88002eSDan Williams 663d88002eSDan Williams return nvdimm_bus->module; 673d88002eSDan Williams } 683d88002eSDan Williams return NULL; 693d88002eSDan Williams } 703d88002eSDan Williams 71eaf96153SDan Williams static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus) 72eaf96153SDan Williams { 73eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 74eaf96153SDan Williams nvdimm_bus->probe_active++; 75eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 76eaf96153SDan Williams } 77eaf96153SDan Williams 78eaf96153SDan Williams static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus) 79eaf96153SDan Williams { 80eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 81eaf96153SDan Williams if (--nvdimm_bus->probe_active == 0) 82eaf96153SDan Williams wake_up(&nvdimm_bus->probe_wait); 83eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 84eaf96153SDan Williams } 85eaf96153SDan Williams 864d88a97aSDan Williams static int nvdimm_bus_probe(struct device *dev) 874d88a97aSDan Williams { 884d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 893d88002eSDan Williams struct module *provider = to_bus_provider(dev); 904d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 914d88a97aSDan Williams int rc; 924d88a97aSDan Williams 933d88002eSDan Williams if (!try_module_get(provider)) 943d88002eSDan Williams return -ENXIO; 953d88002eSDan Williams 96eaf96153SDan Williams nvdimm_bus_probe_start(nvdimm_bus); 974d88a97aSDan Williams rc = nd_drv->probe(dev); 98eaf96153SDan Williams if (rc == 0) 99eaf96153SDan Williams nd_region_probe_success(nvdimm_bus, dev); 100bf9bccc1SDan Williams else 101bf9bccc1SDan Williams nd_region_disable(nvdimm_bus, dev); 102eaf96153SDan Williams nvdimm_bus_probe_end(nvdimm_bus); 103eaf96153SDan Williams 1044d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name, 1054d88a97aSDan Williams dev_name(dev), rc); 1063d88002eSDan Williams if (rc != 0) 1073d88002eSDan Williams module_put(provider); 1084d88a97aSDan Williams return rc; 1094d88a97aSDan Williams } 1104d88a97aSDan Williams 1114d88a97aSDan Williams static int nvdimm_bus_remove(struct device *dev) 1124d88a97aSDan Williams { 1134d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 1143d88002eSDan Williams struct module *provider = to_bus_provider(dev); 1154d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 1164d88a97aSDan Williams int rc; 1174d88a97aSDan Williams 1184d88a97aSDan Williams rc = nd_drv->remove(dev); 119eaf96153SDan Williams nd_region_disable(nvdimm_bus, dev); 120eaf96153SDan Williams 1214d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name, 1224d88a97aSDan Williams dev_name(dev), rc); 1233d88002eSDan Williams module_put(provider); 1244d88a97aSDan Williams return rc; 1254d88a97aSDan Williams } 1264d88a97aSDan Williams 1274d88a97aSDan Williams static struct bus_type nvdimm_bus_type = { 128e6dfb2deSDan Williams .name = "nd", 1294d88a97aSDan Williams .uevent = nvdimm_bus_uevent, 1304d88a97aSDan Williams .match = nvdimm_bus_match, 1314d88a97aSDan Williams .probe = nvdimm_bus_probe, 1324d88a97aSDan Williams .remove = nvdimm_bus_remove, 133e6dfb2deSDan Williams }; 134e6dfb2deSDan Williams 1354d88a97aSDan Williams static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 1364d88a97aSDan Williams 1374d88a97aSDan Williams void nd_synchronize(void) 1384d88a97aSDan Williams { 1394d88a97aSDan Williams async_synchronize_full_domain(&nd_async_domain); 1404d88a97aSDan Williams } 1414d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_synchronize); 1424d88a97aSDan Williams 1434d88a97aSDan Williams static void nd_async_device_register(void *d, async_cookie_t cookie) 1444d88a97aSDan Williams { 1454d88a97aSDan Williams struct device *dev = d; 1464d88a97aSDan Williams 1474d88a97aSDan Williams if (device_add(dev) != 0) { 1484d88a97aSDan Williams dev_err(dev, "%s: failed\n", __func__); 1494d88a97aSDan Williams put_device(dev); 1504d88a97aSDan Williams } 1514d88a97aSDan Williams put_device(dev); 1524d88a97aSDan Williams } 1534d88a97aSDan Williams 1544d88a97aSDan Williams static void nd_async_device_unregister(void *d, async_cookie_t cookie) 1554d88a97aSDan Williams { 1564d88a97aSDan Williams struct device *dev = d; 1574d88a97aSDan Williams 1580ba1c634SDan Williams /* flush bus operations before delete */ 1590ba1c634SDan Williams nvdimm_bus_lock(dev); 1600ba1c634SDan Williams nvdimm_bus_unlock(dev); 1610ba1c634SDan Williams 1624d88a97aSDan Williams device_unregister(dev); 1634d88a97aSDan Williams put_device(dev); 1644d88a97aSDan Williams } 1654d88a97aSDan Williams 1664d88a97aSDan Williams void nd_device_register(struct device *dev) 1674d88a97aSDan Williams { 1684d88a97aSDan Williams dev->bus = &nvdimm_bus_type; 1694d88a97aSDan Williams device_initialize(dev); 1704d88a97aSDan Williams get_device(dev); 1714d88a97aSDan Williams async_schedule_domain(nd_async_device_register, dev, 1724d88a97aSDan Williams &nd_async_domain); 1734d88a97aSDan Williams } 1744d88a97aSDan Williams EXPORT_SYMBOL(nd_device_register); 1754d88a97aSDan Williams 1764d88a97aSDan Williams void nd_device_unregister(struct device *dev, enum nd_async_mode mode) 1774d88a97aSDan Williams { 1784d88a97aSDan Williams switch (mode) { 1794d88a97aSDan Williams case ND_ASYNC: 1804d88a97aSDan Williams get_device(dev); 1814d88a97aSDan Williams async_schedule_domain(nd_async_device_unregister, dev, 1824d88a97aSDan Williams &nd_async_domain); 1834d88a97aSDan Williams break; 1844d88a97aSDan Williams case ND_SYNC: 1854d88a97aSDan Williams nd_synchronize(); 1864d88a97aSDan Williams device_unregister(dev); 1874d88a97aSDan Williams break; 1884d88a97aSDan Williams } 1894d88a97aSDan Williams } 1904d88a97aSDan Williams EXPORT_SYMBOL(nd_device_unregister); 1914d88a97aSDan Williams 1924d88a97aSDan Williams /** 1934d88a97aSDan Williams * __nd_driver_register() - register a region or a namespace driver 1944d88a97aSDan Williams * @nd_drv: driver to register 1954d88a97aSDan Williams * @owner: automatically set by nd_driver_register() macro 1964d88a97aSDan Williams * @mod_name: automatically set by nd_driver_register() macro 1974d88a97aSDan Williams */ 1984d88a97aSDan Williams int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner, 1994d88a97aSDan Williams const char *mod_name) 2004d88a97aSDan Williams { 2014d88a97aSDan Williams struct device_driver *drv = &nd_drv->drv; 2024d88a97aSDan Williams 2034d88a97aSDan Williams if (!nd_drv->type) { 2044d88a97aSDan Williams pr_debug("driver type bitmask not set (%pf)\n", 2054d88a97aSDan Williams __builtin_return_address(0)); 2064d88a97aSDan Williams return -EINVAL; 2074d88a97aSDan Williams } 2084d88a97aSDan Williams 2094d88a97aSDan Williams if (!nd_drv->probe || !nd_drv->remove) { 2104d88a97aSDan Williams pr_debug("->probe() and ->remove() must be specified\n"); 2114d88a97aSDan Williams return -EINVAL; 2124d88a97aSDan Williams } 2134d88a97aSDan Williams 2144d88a97aSDan Williams drv->bus = &nvdimm_bus_type; 2154d88a97aSDan Williams drv->owner = owner; 2164d88a97aSDan Williams drv->mod_name = mod_name; 2174d88a97aSDan Williams 2184d88a97aSDan Williams return driver_register(drv); 2194d88a97aSDan Williams } 2204d88a97aSDan Williams EXPORT_SYMBOL(__nd_driver_register); 2214d88a97aSDan Williams 2224d88a97aSDan Williams static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 2234d88a97aSDan Williams char *buf) 2244d88a97aSDan Williams { 2254d88a97aSDan Williams return sprintf(buf, ND_DEVICE_MODALIAS_FMT "\n", 2264d88a97aSDan Williams to_nd_device_type(dev)); 2274d88a97aSDan Williams } 2284d88a97aSDan Williams static DEVICE_ATTR_RO(modalias); 2294d88a97aSDan Williams 2304d88a97aSDan Williams static ssize_t devtype_show(struct device *dev, struct device_attribute *attr, 2314d88a97aSDan Williams char *buf) 2324d88a97aSDan Williams { 2334d88a97aSDan Williams return sprintf(buf, "%s\n", dev->type->name); 2344d88a97aSDan Williams } 2354d88a97aSDan Williams static DEVICE_ATTR_RO(devtype); 2364d88a97aSDan Williams 2374d88a97aSDan Williams static struct attribute *nd_device_attributes[] = { 2384d88a97aSDan Williams &dev_attr_modalias.attr, 2394d88a97aSDan Williams &dev_attr_devtype.attr, 2404d88a97aSDan Williams NULL, 2414d88a97aSDan Williams }; 2424d88a97aSDan Williams 2434d88a97aSDan Williams /** 2444d88a97aSDan Williams * nd_device_attribute_group - generic attributes for all devices on an nd bus 2454d88a97aSDan Williams */ 2464d88a97aSDan Williams struct attribute_group nd_device_attribute_group = { 2474d88a97aSDan Williams .attrs = nd_device_attributes, 2484d88a97aSDan Williams }; 2494d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_device_attribute_group); 2504d88a97aSDan Williams 25145def22cSDan Williams int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) 25245def22cSDan Williams { 25345def22cSDan Williams dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id); 25445def22cSDan Williams struct device *dev; 25545def22cSDan Williams 25645def22cSDan Williams dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus, 25745def22cSDan Williams "ndctl%d", nvdimm_bus->id); 25845def22cSDan Williams 25945def22cSDan Williams if (IS_ERR(dev)) { 26045def22cSDan Williams dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld\n", 26145def22cSDan Williams nvdimm_bus->id, PTR_ERR(dev)); 26245def22cSDan Williams return PTR_ERR(dev); 26345def22cSDan Williams } 26445def22cSDan Williams return 0; 26545def22cSDan Williams } 26645def22cSDan Williams 26745def22cSDan Williams void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus) 26845def22cSDan Williams { 26945def22cSDan Williams device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id)); 27045def22cSDan Williams } 27145def22cSDan Williams 27262232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = { 27362232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 27462232e45SDan Williams [ND_CMD_SMART] = { 27562232e45SDan Williams .out_num = 2, 27662232e45SDan Williams .out_sizes = { 4, 8, }, 27762232e45SDan Williams }, 27862232e45SDan Williams [ND_CMD_SMART_THRESHOLD] = { 27962232e45SDan Williams .out_num = 2, 28062232e45SDan Williams .out_sizes = { 4, 8, }, 28162232e45SDan Williams }, 28262232e45SDan Williams [ND_CMD_DIMM_FLAGS] = { 28362232e45SDan Williams .out_num = 2, 28462232e45SDan Williams .out_sizes = { 4, 4 }, 28562232e45SDan Williams }, 28662232e45SDan Williams [ND_CMD_GET_CONFIG_SIZE] = { 28762232e45SDan Williams .out_num = 3, 28862232e45SDan Williams .out_sizes = { 4, 4, 4, }, 28962232e45SDan Williams }, 29062232e45SDan Williams [ND_CMD_GET_CONFIG_DATA] = { 29162232e45SDan Williams .in_num = 2, 29262232e45SDan Williams .in_sizes = { 4, 4, }, 29362232e45SDan Williams .out_num = 2, 29462232e45SDan Williams .out_sizes = { 4, UINT_MAX, }, 29562232e45SDan Williams }, 29662232e45SDan Williams [ND_CMD_SET_CONFIG_DATA] = { 29762232e45SDan Williams .in_num = 3, 29862232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 29962232e45SDan Williams .out_num = 1, 30062232e45SDan Williams .out_sizes = { 4, }, 30162232e45SDan Williams }, 30262232e45SDan Williams [ND_CMD_VENDOR] = { 30362232e45SDan Williams .in_num = 3, 30462232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 30562232e45SDan Williams .out_num = 3, 30662232e45SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 30762232e45SDan Williams }, 30862232e45SDan Williams }; 30962232e45SDan Williams 31062232e45SDan Williams const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) 31162232e45SDan Williams { 31262232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_dimm_descs)) 31362232e45SDan Williams return &__nd_cmd_dimm_descs[cmd]; 31462232e45SDan Williams return NULL; 31562232e45SDan Williams } 31662232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_dimm_desc); 31762232e45SDan Williams 31862232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { 31962232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 32062232e45SDan Williams [ND_CMD_ARS_CAP] = { 32162232e45SDan Williams .in_num = 2, 32262232e45SDan Williams .in_sizes = { 8, 8, }, 32362232e45SDan Williams .out_num = 2, 32462232e45SDan Williams .out_sizes = { 4, 4, }, 32562232e45SDan Williams }, 32662232e45SDan Williams [ND_CMD_ARS_START] = { 32762232e45SDan Williams .in_num = 4, 32862232e45SDan Williams .in_sizes = { 8, 8, 2, 6, }, 32962232e45SDan Williams .out_num = 1, 33062232e45SDan Williams .out_sizes = { 4, }, 33162232e45SDan Williams }, 33262232e45SDan Williams [ND_CMD_ARS_STATUS] = { 33362232e45SDan Williams .out_num = 2, 33462232e45SDan Williams .out_sizes = { 4, UINT_MAX, }, 33562232e45SDan Williams }, 33662232e45SDan Williams }; 33762232e45SDan Williams 33862232e45SDan Williams const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) 33962232e45SDan Williams { 34062232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_bus_descs)) 34162232e45SDan Williams return &__nd_cmd_bus_descs[cmd]; 34262232e45SDan Williams return NULL; 34362232e45SDan Williams } 34462232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_bus_desc); 34562232e45SDan Williams 34662232e45SDan Williams u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, 34762232e45SDan Williams const struct nd_cmd_desc *desc, int idx, void *buf) 34862232e45SDan Williams { 34962232e45SDan Williams if (idx >= desc->in_num) 35062232e45SDan Williams return UINT_MAX; 35162232e45SDan Williams 35262232e45SDan Williams if (desc->in_sizes[idx] < UINT_MAX) 35362232e45SDan Williams return desc->in_sizes[idx]; 35462232e45SDan Williams 35562232e45SDan Williams if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA && idx == 2) { 35662232e45SDan Williams struct nd_cmd_set_config_hdr *hdr = buf; 35762232e45SDan Williams 35862232e45SDan Williams return hdr->in_length; 35962232e45SDan Williams } else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) { 36062232e45SDan Williams struct nd_cmd_vendor_hdr *hdr = buf; 36162232e45SDan Williams 36262232e45SDan Williams return hdr->in_length; 36362232e45SDan Williams } 36462232e45SDan Williams 36562232e45SDan Williams return UINT_MAX; 36662232e45SDan Williams } 36762232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_in_size); 36862232e45SDan Williams 36962232e45SDan Williams u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd, 37062232e45SDan Williams const struct nd_cmd_desc *desc, int idx, const u32 *in_field, 37162232e45SDan Williams const u32 *out_field) 37262232e45SDan Williams { 37362232e45SDan Williams if (idx >= desc->out_num) 37462232e45SDan Williams return UINT_MAX; 37562232e45SDan Williams 37662232e45SDan Williams if (desc->out_sizes[idx] < UINT_MAX) 37762232e45SDan Williams return desc->out_sizes[idx]; 37862232e45SDan Williams 37962232e45SDan Williams if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && idx == 1) 38062232e45SDan Williams return in_field[1]; 38162232e45SDan Williams else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) 38262232e45SDan Williams return out_field[1]; 38362232e45SDan Williams else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 1) 38462232e45SDan Williams return ND_CMD_ARS_STATUS_MAX; 38562232e45SDan Williams 38662232e45SDan Williams return UINT_MAX; 38762232e45SDan Williams } 38862232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_out_size); 38962232e45SDan Williams 390bf9bccc1SDan Williams void wait_nvdimm_bus_probe_idle(struct device *dev) 391eaf96153SDan Williams { 392bf9bccc1SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 393bf9bccc1SDan Williams 394eaf96153SDan Williams do { 395eaf96153SDan Williams if (nvdimm_bus->probe_active == 0) 396eaf96153SDan Williams break; 397eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 398eaf96153SDan Williams wait_event(nvdimm_bus->probe_wait, 399eaf96153SDan Williams nvdimm_bus->probe_active == 0); 400eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 401eaf96153SDan Williams } while (true); 402eaf96153SDan Williams } 403eaf96153SDan Williams 404eaf96153SDan Williams /* set_config requires an idle interleave set */ 405eaf96153SDan Williams static int nd_cmd_clear_to_send(struct nvdimm *nvdimm, unsigned int cmd) 406eaf96153SDan Williams { 407eaf96153SDan Williams struct nvdimm_bus *nvdimm_bus; 408eaf96153SDan Williams 409eaf96153SDan Williams if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) 410eaf96153SDan Williams return 0; 411eaf96153SDan Williams 412eaf96153SDan Williams nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev); 413bf9bccc1SDan Williams wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev); 414eaf96153SDan Williams 415eaf96153SDan Williams if (atomic_read(&nvdimm->busy)) 416eaf96153SDan Williams return -EBUSY; 417eaf96153SDan Williams return 0; 418eaf96153SDan Williams } 419eaf96153SDan Williams 42062232e45SDan Williams static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 42162232e45SDan Williams int read_only, unsigned int ioctl_cmd, unsigned long arg) 42262232e45SDan Williams { 42362232e45SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 42462232e45SDan Williams size_t buf_len = 0, in_len = 0, out_len = 0; 42562232e45SDan Williams static char out_env[ND_CMD_MAX_ENVELOPE]; 42662232e45SDan Williams static char in_env[ND_CMD_MAX_ENVELOPE]; 42762232e45SDan Williams const struct nd_cmd_desc *desc = NULL; 42862232e45SDan Williams unsigned int cmd = _IOC_NR(ioctl_cmd); 42962232e45SDan Williams void __user *p = (void __user *) arg; 43062232e45SDan Williams struct device *dev = &nvdimm_bus->dev; 43162232e45SDan Williams const char *cmd_name, *dimm_name; 43262232e45SDan Williams unsigned long dsm_mask; 43362232e45SDan Williams void *buf; 43462232e45SDan Williams int rc, i; 43562232e45SDan Williams 43662232e45SDan Williams if (nvdimm) { 43762232e45SDan Williams desc = nd_cmd_dimm_desc(cmd); 43862232e45SDan Williams cmd_name = nvdimm_cmd_name(cmd); 43962232e45SDan Williams dsm_mask = nvdimm->dsm_mask ? *(nvdimm->dsm_mask) : 0; 44062232e45SDan Williams dimm_name = dev_name(&nvdimm->dev); 44162232e45SDan Williams } else { 44262232e45SDan Williams desc = nd_cmd_bus_desc(cmd); 44362232e45SDan Williams cmd_name = nvdimm_bus_cmd_name(cmd); 44462232e45SDan Williams dsm_mask = nd_desc->dsm_mask; 44562232e45SDan Williams dimm_name = "bus"; 44662232e45SDan Williams } 44762232e45SDan Williams 44862232e45SDan Williams if (!desc || (desc->out_num + desc->in_num == 0) || 44962232e45SDan Williams !test_bit(cmd, &dsm_mask)) 45062232e45SDan Williams return -ENOTTY; 45162232e45SDan Williams 45262232e45SDan Williams /* fail write commands (when read-only) */ 45362232e45SDan Williams if (read_only) 45462232e45SDan Williams switch (ioctl_cmd) { 45562232e45SDan Williams case ND_IOCTL_VENDOR: 45662232e45SDan Williams case ND_IOCTL_SET_CONFIG_DATA: 45762232e45SDan Williams case ND_IOCTL_ARS_START: 45862232e45SDan Williams dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 45962232e45SDan Williams nvdimm ? nvdimm_cmd_name(cmd) 46062232e45SDan Williams : nvdimm_bus_cmd_name(cmd)); 46162232e45SDan Williams return -EPERM; 46262232e45SDan Williams default: 46362232e45SDan Williams break; 46462232e45SDan Williams } 46562232e45SDan Williams 46662232e45SDan Williams /* process an input envelope */ 46762232e45SDan Williams for (i = 0; i < desc->in_num; i++) { 46862232e45SDan Williams u32 in_size, copy; 46962232e45SDan Williams 47062232e45SDan Williams in_size = nd_cmd_in_size(nvdimm, cmd, desc, i, in_env); 47162232e45SDan Williams if (in_size == UINT_MAX) { 47262232e45SDan Williams dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n", 47362232e45SDan Williams __func__, dimm_name, cmd_name, i); 47462232e45SDan Williams return -ENXIO; 47562232e45SDan Williams } 47662232e45SDan Williams if (!access_ok(VERIFY_READ, p + in_len, in_size)) 47762232e45SDan Williams return -EFAULT; 47862232e45SDan Williams if (in_len < sizeof(in_env)) 47962232e45SDan Williams copy = min_t(u32, sizeof(in_env) - in_len, in_size); 48062232e45SDan Williams else 48162232e45SDan Williams copy = 0; 48262232e45SDan Williams if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) 48362232e45SDan Williams return -EFAULT; 48462232e45SDan Williams in_len += in_size; 48562232e45SDan Williams } 48662232e45SDan Williams 48762232e45SDan Williams /* process an output envelope */ 48862232e45SDan Williams for (i = 0; i < desc->out_num; i++) { 48962232e45SDan Williams u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, 49062232e45SDan Williams (u32 *) in_env, (u32 *) out_env); 49162232e45SDan Williams u32 copy; 49262232e45SDan Williams 49362232e45SDan Williams if (out_size == UINT_MAX) { 49462232e45SDan Williams dev_dbg(dev, "%s:%s unknown output size cmd: %s field: %d\n", 49562232e45SDan Williams __func__, dimm_name, cmd_name, i); 49662232e45SDan Williams return -EFAULT; 49762232e45SDan Williams } 49862232e45SDan Williams if (!access_ok(VERIFY_WRITE, p + in_len + out_len, out_size)) 49962232e45SDan Williams return -EFAULT; 50062232e45SDan Williams if (out_len < sizeof(out_env)) 50162232e45SDan Williams copy = min_t(u32, sizeof(out_env) - out_len, out_size); 50262232e45SDan Williams else 50362232e45SDan Williams copy = 0; 50462232e45SDan Williams if (copy && copy_from_user(&out_env[out_len], 50562232e45SDan Williams p + in_len + out_len, copy)) 50662232e45SDan Williams return -EFAULT; 50762232e45SDan Williams out_len += out_size; 50862232e45SDan Williams } 50962232e45SDan Williams 51062232e45SDan Williams buf_len = out_len + in_len; 51162232e45SDan Williams if (!access_ok(VERIFY_WRITE, p, sizeof(buf_len))) 51262232e45SDan Williams return -EFAULT; 51362232e45SDan Williams 51462232e45SDan Williams if (buf_len > ND_IOCTL_MAX_BUFLEN) { 51562232e45SDan Williams dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__, 51662232e45SDan Williams dimm_name, cmd_name, buf_len, 51762232e45SDan Williams ND_IOCTL_MAX_BUFLEN); 51862232e45SDan Williams return -EINVAL; 51962232e45SDan Williams } 52062232e45SDan Williams 52162232e45SDan Williams buf = vmalloc(buf_len); 52262232e45SDan Williams if (!buf) 52362232e45SDan Williams return -ENOMEM; 52462232e45SDan Williams 52562232e45SDan Williams if (copy_from_user(buf, p, buf_len)) { 52662232e45SDan Williams rc = -EFAULT; 52762232e45SDan Williams goto out; 52862232e45SDan Williams } 52962232e45SDan Williams 530eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 531eaf96153SDan Williams rc = nd_cmd_clear_to_send(nvdimm, cmd); 532eaf96153SDan Williams if (rc) 533eaf96153SDan Williams goto out_unlock; 534eaf96153SDan Williams 53562232e45SDan Williams rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len); 53662232e45SDan Williams if (rc < 0) 537eaf96153SDan Williams goto out_unlock; 53862232e45SDan Williams if (copy_to_user(p, buf, buf_len)) 53962232e45SDan Williams rc = -EFAULT; 540eaf96153SDan Williams out_unlock: 541eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 54262232e45SDan Williams out: 54362232e45SDan Williams vfree(buf); 54462232e45SDan Williams return rc; 54562232e45SDan Williams } 54662232e45SDan Williams 54745def22cSDan Williams static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 54845def22cSDan Williams { 54962232e45SDan Williams long id = (long) file->private_data; 55062232e45SDan Williams int rc = -ENXIO, read_only; 55162232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 55262232e45SDan Williams 55362232e45SDan Williams read_only = (O_RDWR != (file->f_flags & O_ACCMODE)); 55462232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 55562232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 55662232e45SDan Williams if (nvdimm_bus->id == id) { 55762232e45SDan Williams rc = __nd_ioctl(nvdimm_bus, NULL, read_only, cmd, arg); 55862232e45SDan Williams break; 55962232e45SDan Williams } 56062232e45SDan Williams } 56162232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 56262232e45SDan Williams 56362232e45SDan Williams return rc; 56462232e45SDan Williams } 56562232e45SDan Williams 56662232e45SDan Williams static int match_dimm(struct device *dev, void *data) 56762232e45SDan Williams { 56862232e45SDan Williams long id = (long) data; 56962232e45SDan Williams 57062232e45SDan Williams if (is_nvdimm(dev)) { 57162232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 57262232e45SDan Williams 57362232e45SDan Williams return nvdimm->id == id; 57462232e45SDan Williams } 57562232e45SDan Williams 57662232e45SDan Williams return 0; 57762232e45SDan Williams } 57862232e45SDan Williams 57962232e45SDan Williams static long nvdimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 58062232e45SDan Williams { 58162232e45SDan Williams int rc = -ENXIO, read_only; 58262232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 58362232e45SDan Williams 58462232e45SDan Williams read_only = (O_RDWR != (file->f_flags & O_ACCMODE)); 58562232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 58662232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 58762232e45SDan Williams struct device *dev = device_find_child(&nvdimm_bus->dev, 58862232e45SDan Williams file->private_data, match_dimm); 58962232e45SDan Williams struct nvdimm *nvdimm; 59062232e45SDan Williams 59162232e45SDan Williams if (!dev) 59262232e45SDan Williams continue; 59362232e45SDan Williams 59462232e45SDan Williams nvdimm = to_nvdimm(dev); 59562232e45SDan Williams rc = __nd_ioctl(nvdimm_bus, nvdimm, read_only, cmd, arg); 59662232e45SDan Williams put_device(dev); 59762232e45SDan Williams break; 59862232e45SDan Williams } 59962232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 60062232e45SDan Williams 60162232e45SDan Williams return rc; 60262232e45SDan Williams } 60362232e45SDan Williams 60462232e45SDan Williams static int nd_open(struct inode *inode, struct file *file) 60562232e45SDan Williams { 60662232e45SDan Williams long minor = iminor(inode); 60762232e45SDan Williams 60862232e45SDan Williams file->private_data = (void *) minor; 60962232e45SDan Williams return 0; 61045def22cSDan Williams } 61145def22cSDan Williams 61245def22cSDan Williams static const struct file_operations nvdimm_bus_fops = { 61345def22cSDan Williams .owner = THIS_MODULE, 61462232e45SDan Williams .open = nd_open, 61545def22cSDan Williams .unlocked_ioctl = nd_ioctl, 61645def22cSDan Williams .compat_ioctl = nd_ioctl, 61745def22cSDan Williams .llseek = noop_llseek, 61845def22cSDan Williams }; 61945def22cSDan Williams 62062232e45SDan Williams static const struct file_operations nvdimm_fops = { 62162232e45SDan Williams .owner = THIS_MODULE, 62262232e45SDan Williams .open = nd_open, 62362232e45SDan Williams .unlocked_ioctl = nvdimm_ioctl, 62462232e45SDan Williams .compat_ioctl = nvdimm_ioctl, 62562232e45SDan Williams .llseek = noop_llseek, 62662232e45SDan Williams }; 62762232e45SDan Williams 62845def22cSDan Williams int __init nvdimm_bus_init(void) 62945def22cSDan Williams { 63045def22cSDan Williams int rc; 63145def22cSDan Williams 632e6dfb2deSDan Williams rc = bus_register(&nvdimm_bus_type); 633e6dfb2deSDan Williams if (rc) 634e6dfb2deSDan Williams return rc; 635e6dfb2deSDan Williams 63645def22cSDan Williams rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops); 63745def22cSDan Williams if (rc < 0) 63862232e45SDan Williams goto err_bus_chrdev; 63945def22cSDan Williams nvdimm_bus_major = rc; 64045def22cSDan Williams 64162232e45SDan Williams rc = register_chrdev(0, "dimmctl", &nvdimm_fops); 64262232e45SDan Williams if (rc < 0) 64362232e45SDan Williams goto err_dimm_chrdev; 64462232e45SDan Williams nvdimm_major = rc; 64562232e45SDan Williams 64645def22cSDan Williams nd_class = class_create(THIS_MODULE, "nd"); 64745def22cSDan Williams if (IS_ERR(nd_class)) 64845def22cSDan Williams goto err_class; 64945def22cSDan Williams 65045def22cSDan Williams return 0; 65145def22cSDan Williams 65245def22cSDan Williams err_class: 65362232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 65462232e45SDan Williams err_dimm_chrdev: 65545def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 65662232e45SDan Williams err_bus_chrdev: 657e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 65845def22cSDan Williams 65945def22cSDan Williams return rc; 66045def22cSDan Williams } 66145def22cSDan Williams 6624d88a97aSDan Williams void nvdimm_bus_exit(void) 66345def22cSDan Williams { 66445def22cSDan Williams class_destroy(nd_class); 66545def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 66662232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 667e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 66845def22cSDan Williams } 669