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> 178c2f7e86SDan Williams #include <linux/blkdev.h> 1845def22cSDan Williams #include <linux/fcntl.h> 19e6dfb2deSDan Williams #include <linux/async.h> 208c2f7e86SDan Williams #include <linux/genhd.h> 2162232e45SDan Williams #include <linux/ndctl.h> 224d88a97aSDan Williams #include <linux/sched.h> 2345def22cSDan Williams #include <linux/slab.h> 2445def22cSDan Williams #include <linux/fs.h> 2545def22cSDan Williams #include <linux/io.h> 2662232e45SDan Williams #include <linux/mm.h> 274d88a97aSDan Williams #include <linux/nd.h> 2845def22cSDan Williams #include "nd-core.h" 294d88a97aSDan Williams #include "nd.h" 3045def22cSDan Williams 3162232e45SDan Williams int nvdimm_major; 3245def22cSDan Williams static int nvdimm_bus_major; 3345def22cSDan Williams static struct class *nd_class; 3445def22cSDan Williams 354d88a97aSDan Williams static int to_nd_device_type(struct device *dev) 364d88a97aSDan Williams { 374d88a97aSDan Williams if (is_nvdimm(dev)) 384d88a97aSDan Williams return ND_DEVICE_DIMM; 393d88002eSDan Williams else if (is_nd_pmem(dev)) 403d88002eSDan Williams return ND_DEVICE_REGION_PMEM; 413d88002eSDan Williams else if (is_nd_blk(dev)) 423d88002eSDan Williams return ND_DEVICE_REGION_BLK; 433d88002eSDan Williams else if (is_nd_pmem(dev->parent) || is_nd_blk(dev->parent)) 443d88002eSDan Williams return nd_region_to_nstype(to_nd_region(dev->parent)); 454d88a97aSDan Williams 464d88a97aSDan Williams return 0; 474d88a97aSDan Williams } 484d88a97aSDan Williams 494d88a97aSDan Williams static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env) 504d88a97aSDan Williams { 5141d7a6d6SToshi Kani /* 5241d7a6d6SToshi Kani * Ensure that region devices always have their numa node set as 5341d7a6d6SToshi Kani * early as possible. 5441d7a6d6SToshi Kani */ 5541d7a6d6SToshi Kani if (is_nd_pmem(dev) || is_nd_blk(dev)) 5641d7a6d6SToshi Kani set_dev_node(dev, to_nd_region(dev)->numa_node); 574d88a97aSDan Williams return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT, 584d88a97aSDan Williams to_nd_device_type(dev)); 594d88a97aSDan Williams } 604d88a97aSDan Williams 614d88a97aSDan Williams static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 624d88a97aSDan Williams { 634d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 644d88a97aSDan Williams 6582ec2ba2SDan Williams return !!test_bit(to_nd_device_type(dev), &nd_drv->type); 664d88a97aSDan Williams } 674d88a97aSDan Williams 683d88002eSDan Williams static struct module *to_bus_provider(struct device *dev) 693d88002eSDan Williams { 703d88002eSDan Williams /* pin bus providers while regions are enabled */ 713d88002eSDan Williams if (is_nd_pmem(dev) || is_nd_blk(dev)) { 723d88002eSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 733d88002eSDan Williams 743d88002eSDan Williams return nvdimm_bus->module; 753d88002eSDan Williams } 763d88002eSDan Williams return NULL; 773d88002eSDan Williams } 783d88002eSDan Williams 79eaf96153SDan Williams static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus) 80eaf96153SDan Williams { 81eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 82eaf96153SDan Williams nvdimm_bus->probe_active++; 83eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 84eaf96153SDan Williams } 85eaf96153SDan Williams 86eaf96153SDan Williams static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus) 87eaf96153SDan Williams { 88eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 89eaf96153SDan Williams if (--nvdimm_bus->probe_active == 0) 90eaf96153SDan Williams wake_up(&nvdimm_bus->probe_wait); 91eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 92eaf96153SDan Williams } 93eaf96153SDan Williams 944d88a97aSDan Williams static int nvdimm_bus_probe(struct device *dev) 954d88a97aSDan Williams { 964d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 973d88002eSDan Williams struct module *provider = to_bus_provider(dev); 984d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 994d88a97aSDan Williams int rc; 1004d88a97aSDan Williams 1013d88002eSDan Williams if (!try_module_get(provider)) 1023d88002eSDan Williams return -ENXIO; 1033d88002eSDan Williams 104eaf96153SDan Williams nvdimm_bus_probe_start(nvdimm_bus); 1054d88a97aSDan Williams rc = nd_drv->probe(dev); 106eaf96153SDan Williams if (rc == 0) 107eaf96153SDan Williams nd_region_probe_success(nvdimm_bus, dev); 108bf9bccc1SDan Williams else 109bf9bccc1SDan Williams nd_region_disable(nvdimm_bus, dev); 110eaf96153SDan Williams nvdimm_bus_probe_end(nvdimm_bus); 111eaf96153SDan Williams 1124d88a97aSDan Williams dev_dbg(&nvdimm_bus->dev, "%s.probe(%s) = %d\n", dev->driver->name, 1134d88a97aSDan Williams dev_name(dev), rc); 1148c2f7e86SDan Williams 1153d88002eSDan Williams if (rc != 0) 1163d88002eSDan Williams module_put(provider); 1174d88a97aSDan Williams return rc; 1184d88a97aSDan Williams } 1194d88a97aSDan Williams 1204d88a97aSDan Williams static int nvdimm_bus_remove(struct device *dev) 1214d88a97aSDan Williams { 1224d88a97aSDan Williams struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver); 1233d88002eSDan Williams struct module *provider = to_bus_provider(dev); 1244d88a97aSDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 1254d88a97aSDan Williams int rc; 1264d88a97aSDan Williams 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 13671999466SDan Williams void nd_device_notify(struct device *dev, enum nvdimm_event event) 13771999466SDan Williams { 13871999466SDan Williams device_lock(dev); 13971999466SDan Williams if (dev->driver) { 14071999466SDan Williams struct nd_device_driver *nd_drv; 14171999466SDan Williams 14271999466SDan Williams nd_drv = to_nd_device_driver(dev->driver); 14371999466SDan Williams if (nd_drv->notify) 14471999466SDan Williams nd_drv->notify(dev, event); 14571999466SDan Williams } 14671999466SDan Williams device_unlock(dev); 14771999466SDan Williams } 14871999466SDan Williams EXPORT_SYMBOL(nd_device_notify); 14971999466SDan Williams 15071999466SDan Williams void nvdimm_region_notify(struct nd_region *nd_region, enum nvdimm_event event) 15171999466SDan Williams { 15271999466SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 15371999466SDan Williams 15471999466SDan Williams if (!nvdimm_bus) 15571999466SDan Williams return; 15671999466SDan Williams 15771999466SDan Williams /* caller is responsible for holding a reference on the device */ 15871999466SDan Williams nd_device_notify(&nd_region->dev, event); 15971999466SDan Williams } 16071999466SDan Williams EXPORT_SYMBOL_GPL(nvdimm_region_notify); 16171999466SDan Williams 16259e64739SDan Williams long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, 16359e64739SDan Williams unsigned int len) 16459e64739SDan Williams { 16559e64739SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 16659e64739SDan Williams struct nvdimm_bus_descriptor *nd_desc; 16759e64739SDan Williams struct nd_cmd_clear_error clear_err; 16859e64739SDan Williams struct nd_cmd_ars_cap ars_cap; 16959e64739SDan Williams u32 clear_err_unit, mask; 17059e64739SDan Williams int cmd_rc, rc; 17159e64739SDan Williams 17259e64739SDan Williams if (!nvdimm_bus) 17359e64739SDan Williams return -ENXIO; 17459e64739SDan Williams 17559e64739SDan Williams nd_desc = nvdimm_bus->nd_desc; 17659e64739SDan Williams if (!nd_desc->ndctl) 17759e64739SDan Williams return -ENXIO; 17859e64739SDan Williams 17959e64739SDan Williams memset(&ars_cap, 0, sizeof(ars_cap)); 18059e64739SDan Williams ars_cap.address = phys; 18159e64739SDan Williams ars_cap.length = len; 18259e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap, 18359e64739SDan Williams sizeof(ars_cap), &cmd_rc); 18459e64739SDan Williams if (rc < 0) 18559e64739SDan Williams return rc; 18659e64739SDan Williams if (cmd_rc < 0) 18759e64739SDan Williams return cmd_rc; 18859e64739SDan Williams clear_err_unit = ars_cap.clear_err_unit; 18959e64739SDan Williams if (!clear_err_unit || !is_power_of_2(clear_err_unit)) 19059e64739SDan Williams return -ENXIO; 19159e64739SDan Williams 19259e64739SDan Williams mask = clear_err_unit - 1; 19359e64739SDan Williams if ((phys | len) & mask) 19459e64739SDan Williams return -ENXIO; 19559e64739SDan Williams memset(&clear_err, 0, sizeof(clear_err)); 19659e64739SDan Williams clear_err.address = phys; 19759e64739SDan Williams clear_err.length = len; 19859e64739SDan Williams rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err, 19959e64739SDan Williams sizeof(clear_err), &cmd_rc); 20059e64739SDan Williams if (rc < 0) 20159e64739SDan Williams return rc; 20259e64739SDan Williams if (cmd_rc < 0) 20359e64739SDan Williams return cmd_rc; 20459e64739SDan Williams return clear_err.cleared; 20559e64739SDan Williams } 20659e64739SDan Williams EXPORT_SYMBOL_GPL(nvdimm_clear_poison); 20759e64739SDan Williams 2084d88a97aSDan Williams static struct bus_type nvdimm_bus_type = { 209e6dfb2deSDan Williams .name = "nd", 2104d88a97aSDan Williams .uevent = nvdimm_bus_uevent, 2114d88a97aSDan Williams .match = nvdimm_bus_match, 2124d88a97aSDan Williams .probe = nvdimm_bus_probe, 2134d88a97aSDan Williams .remove = nvdimm_bus_remove, 214e6dfb2deSDan Williams }; 215e6dfb2deSDan Williams 2164d88a97aSDan Williams static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 2174d88a97aSDan Williams 2184d88a97aSDan Williams void nd_synchronize(void) 2194d88a97aSDan Williams { 2204d88a97aSDan Williams async_synchronize_full_domain(&nd_async_domain); 2214d88a97aSDan Williams } 2224d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_synchronize); 2234d88a97aSDan Williams 2244d88a97aSDan Williams static void nd_async_device_register(void *d, async_cookie_t cookie) 2254d88a97aSDan Williams { 2264d88a97aSDan Williams struct device *dev = d; 2274d88a97aSDan Williams 2284d88a97aSDan Williams if (device_add(dev) != 0) { 2294d88a97aSDan Williams dev_err(dev, "%s: failed\n", __func__); 2304d88a97aSDan Williams put_device(dev); 2314d88a97aSDan Williams } 2324d88a97aSDan Williams put_device(dev); 2334d88a97aSDan Williams } 2344d88a97aSDan Williams 2354d88a97aSDan Williams static void nd_async_device_unregister(void *d, async_cookie_t cookie) 2364d88a97aSDan Williams { 2374d88a97aSDan Williams struct device *dev = d; 2384d88a97aSDan Williams 2390ba1c634SDan Williams /* flush bus operations before delete */ 2400ba1c634SDan Williams nvdimm_bus_lock(dev); 2410ba1c634SDan Williams nvdimm_bus_unlock(dev); 2420ba1c634SDan Williams 2434d88a97aSDan Williams device_unregister(dev); 2444d88a97aSDan Williams put_device(dev); 2454d88a97aSDan Williams } 2464d88a97aSDan Williams 2478c2f7e86SDan Williams void __nd_device_register(struct device *dev) 2484d88a97aSDan Williams { 2494d88a97aSDan Williams dev->bus = &nvdimm_bus_type; 2504d88a97aSDan Williams get_device(dev); 2514d88a97aSDan Williams async_schedule_domain(nd_async_device_register, dev, 2524d88a97aSDan Williams &nd_async_domain); 2534d88a97aSDan Williams } 2548c2f7e86SDan Williams 2558c2f7e86SDan Williams void nd_device_register(struct device *dev) 2568c2f7e86SDan Williams { 2578c2f7e86SDan Williams device_initialize(dev); 2588c2f7e86SDan Williams __nd_device_register(dev); 2598c2f7e86SDan Williams } 2604d88a97aSDan Williams EXPORT_SYMBOL(nd_device_register); 2614d88a97aSDan Williams 2624d88a97aSDan Williams void nd_device_unregister(struct device *dev, enum nd_async_mode mode) 2634d88a97aSDan Williams { 2644d88a97aSDan Williams switch (mode) { 2654d88a97aSDan Williams case ND_ASYNC: 2664d88a97aSDan Williams get_device(dev); 2674d88a97aSDan Williams async_schedule_domain(nd_async_device_unregister, dev, 2684d88a97aSDan Williams &nd_async_domain); 2694d88a97aSDan Williams break; 2704d88a97aSDan Williams case ND_SYNC: 2714d88a97aSDan Williams nd_synchronize(); 2724d88a97aSDan Williams device_unregister(dev); 2734d88a97aSDan Williams break; 2744d88a97aSDan Williams } 2754d88a97aSDan Williams } 2764d88a97aSDan Williams EXPORT_SYMBOL(nd_device_unregister); 2774d88a97aSDan Williams 2784d88a97aSDan Williams /** 2794d88a97aSDan Williams * __nd_driver_register() - register a region or a namespace driver 2804d88a97aSDan Williams * @nd_drv: driver to register 2814d88a97aSDan Williams * @owner: automatically set by nd_driver_register() macro 2824d88a97aSDan Williams * @mod_name: automatically set by nd_driver_register() macro 2834d88a97aSDan Williams */ 2844d88a97aSDan Williams int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner, 2854d88a97aSDan Williams const char *mod_name) 2864d88a97aSDan Williams { 2874d88a97aSDan Williams struct device_driver *drv = &nd_drv->drv; 2884d88a97aSDan Williams 2894d88a97aSDan Williams if (!nd_drv->type) { 2904d88a97aSDan Williams pr_debug("driver type bitmask not set (%pf)\n", 2914d88a97aSDan Williams __builtin_return_address(0)); 2924d88a97aSDan Williams return -EINVAL; 2934d88a97aSDan Williams } 2944d88a97aSDan Williams 2954d88a97aSDan Williams if (!nd_drv->probe || !nd_drv->remove) { 2964d88a97aSDan Williams pr_debug("->probe() and ->remove() must be specified\n"); 2974d88a97aSDan Williams return -EINVAL; 2984d88a97aSDan Williams } 2994d88a97aSDan Williams 3004d88a97aSDan Williams drv->bus = &nvdimm_bus_type; 3014d88a97aSDan Williams drv->owner = owner; 3024d88a97aSDan Williams drv->mod_name = mod_name; 3034d88a97aSDan Williams 3044d88a97aSDan Williams return driver_register(drv); 3054d88a97aSDan Williams } 3064d88a97aSDan Williams EXPORT_SYMBOL(__nd_driver_register); 3074d88a97aSDan Williams 30858138820SDan Williams int nvdimm_revalidate_disk(struct gendisk *disk) 30958138820SDan Williams { 31058138820SDan Williams struct device *dev = disk->driverfs_dev; 31158138820SDan Williams struct nd_region *nd_region = to_nd_region(dev->parent); 31258138820SDan Williams const char *pol = nd_region->ro ? "only" : "write"; 31358138820SDan Williams 31458138820SDan Williams if (nd_region->ro == get_disk_ro(disk)) 31558138820SDan Williams return 0; 31658138820SDan Williams 31758138820SDan Williams dev_info(dev, "%s read-%s, marking %s read-%s\n", 31858138820SDan Williams dev_name(&nd_region->dev), pol, disk->disk_name, pol); 31958138820SDan Williams set_disk_ro(disk, nd_region->ro); 32058138820SDan Williams 32158138820SDan Williams return 0; 32258138820SDan Williams 32358138820SDan Williams } 32458138820SDan Williams EXPORT_SYMBOL(nvdimm_revalidate_disk); 32558138820SDan Williams 3264d88a97aSDan Williams static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 3274d88a97aSDan Williams char *buf) 3284d88a97aSDan Williams { 3294d88a97aSDan Williams return sprintf(buf, ND_DEVICE_MODALIAS_FMT "\n", 3304d88a97aSDan Williams to_nd_device_type(dev)); 3314d88a97aSDan Williams } 3324d88a97aSDan Williams static DEVICE_ATTR_RO(modalias); 3334d88a97aSDan Williams 3344d88a97aSDan Williams static ssize_t devtype_show(struct device *dev, struct device_attribute *attr, 3354d88a97aSDan Williams char *buf) 3364d88a97aSDan Williams { 3374d88a97aSDan Williams return sprintf(buf, "%s\n", dev->type->name); 3384d88a97aSDan Williams } 3394d88a97aSDan Williams static DEVICE_ATTR_RO(devtype); 3404d88a97aSDan Williams 3414d88a97aSDan Williams static struct attribute *nd_device_attributes[] = { 3424d88a97aSDan Williams &dev_attr_modalias.attr, 3434d88a97aSDan Williams &dev_attr_devtype.attr, 3444d88a97aSDan Williams NULL, 3454d88a97aSDan Williams }; 3464d88a97aSDan Williams 3474d88a97aSDan Williams /** 3484d88a97aSDan Williams * nd_device_attribute_group - generic attributes for all devices on an nd bus 3494d88a97aSDan Williams */ 3504d88a97aSDan Williams struct attribute_group nd_device_attribute_group = { 3514d88a97aSDan Williams .attrs = nd_device_attributes, 3524d88a97aSDan Williams }; 3534d88a97aSDan Williams EXPORT_SYMBOL_GPL(nd_device_attribute_group); 3544d88a97aSDan Williams 35574ae66c3SToshi Kani static ssize_t numa_node_show(struct device *dev, 35674ae66c3SToshi Kani struct device_attribute *attr, char *buf) 35774ae66c3SToshi Kani { 35874ae66c3SToshi Kani return sprintf(buf, "%d\n", dev_to_node(dev)); 35974ae66c3SToshi Kani } 36074ae66c3SToshi Kani static DEVICE_ATTR_RO(numa_node); 36174ae66c3SToshi Kani 36274ae66c3SToshi Kani static struct attribute *nd_numa_attributes[] = { 36374ae66c3SToshi Kani &dev_attr_numa_node.attr, 36474ae66c3SToshi Kani NULL, 36574ae66c3SToshi Kani }; 36674ae66c3SToshi Kani 36774ae66c3SToshi Kani static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a, 36874ae66c3SToshi Kani int n) 36974ae66c3SToshi Kani { 37074ae66c3SToshi Kani if (!IS_ENABLED(CONFIG_NUMA)) 37174ae66c3SToshi Kani return 0; 37274ae66c3SToshi Kani 37374ae66c3SToshi Kani return a->mode; 37474ae66c3SToshi Kani } 37574ae66c3SToshi Kani 37674ae66c3SToshi Kani /** 37774ae66c3SToshi Kani * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus 37874ae66c3SToshi Kani */ 37974ae66c3SToshi Kani struct attribute_group nd_numa_attribute_group = { 38074ae66c3SToshi Kani .attrs = nd_numa_attributes, 38174ae66c3SToshi Kani .is_visible = nd_numa_attr_visible, 38274ae66c3SToshi Kani }; 38374ae66c3SToshi Kani EXPORT_SYMBOL_GPL(nd_numa_attribute_group); 38474ae66c3SToshi Kani 38545def22cSDan Williams int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) 38645def22cSDan Williams { 38745def22cSDan Williams dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id); 38845def22cSDan Williams struct device *dev; 38945def22cSDan Williams 39045def22cSDan Williams dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus, 39145def22cSDan Williams "ndctl%d", nvdimm_bus->id); 39245def22cSDan Williams 39345def22cSDan Williams if (IS_ERR(dev)) { 39445def22cSDan Williams dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld\n", 39545def22cSDan Williams nvdimm_bus->id, PTR_ERR(dev)); 39645def22cSDan Williams return PTR_ERR(dev); 39745def22cSDan Williams } 39845def22cSDan Williams return 0; 39945def22cSDan Williams } 40045def22cSDan Williams 40145def22cSDan Williams void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus) 40245def22cSDan Williams { 40345def22cSDan Williams device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id)); 40445def22cSDan Williams } 40545def22cSDan Williams 40662232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = { 40762232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 40862232e45SDan Williams [ND_CMD_SMART] = { 40962232e45SDan Williams .out_num = 2, 41021129112SDan Williams .out_sizes = { 4, 128, }, 41162232e45SDan Williams }, 41262232e45SDan Williams [ND_CMD_SMART_THRESHOLD] = { 41362232e45SDan Williams .out_num = 2, 41462232e45SDan Williams .out_sizes = { 4, 8, }, 41562232e45SDan Williams }, 41662232e45SDan Williams [ND_CMD_DIMM_FLAGS] = { 41762232e45SDan Williams .out_num = 2, 41862232e45SDan Williams .out_sizes = { 4, 4 }, 41962232e45SDan Williams }, 42062232e45SDan Williams [ND_CMD_GET_CONFIG_SIZE] = { 42162232e45SDan Williams .out_num = 3, 42262232e45SDan Williams .out_sizes = { 4, 4, 4, }, 42362232e45SDan Williams }, 42462232e45SDan Williams [ND_CMD_GET_CONFIG_DATA] = { 42562232e45SDan Williams .in_num = 2, 42662232e45SDan Williams .in_sizes = { 4, 4, }, 42762232e45SDan Williams .out_num = 2, 42862232e45SDan Williams .out_sizes = { 4, UINT_MAX, }, 42962232e45SDan Williams }, 43062232e45SDan Williams [ND_CMD_SET_CONFIG_DATA] = { 43162232e45SDan Williams .in_num = 3, 43262232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 43362232e45SDan Williams .out_num = 1, 43462232e45SDan Williams .out_sizes = { 4, }, 43562232e45SDan Williams }, 43662232e45SDan Williams [ND_CMD_VENDOR] = { 43762232e45SDan Williams .in_num = 3, 43862232e45SDan Williams .in_sizes = { 4, 4, UINT_MAX, }, 43962232e45SDan Williams .out_num = 3, 44062232e45SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 44162232e45SDan Williams }, 44231eca76bSDan Williams [ND_CMD_CALL] = { 44331eca76bSDan Williams .in_num = 2, 44431eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 44531eca76bSDan Williams .out_num = 1, 44631eca76bSDan Williams .out_sizes = { UINT_MAX, }, 44731eca76bSDan Williams }, 44862232e45SDan Williams }; 44962232e45SDan Williams 45062232e45SDan Williams const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd) 45162232e45SDan Williams { 45262232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_dimm_descs)) 45362232e45SDan Williams return &__nd_cmd_dimm_descs[cmd]; 45462232e45SDan Williams return NULL; 45562232e45SDan Williams } 45662232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_dimm_desc); 45762232e45SDan Williams 45862232e45SDan Williams static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { 45962232e45SDan Williams [ND_CMD_IMPLEMENTED] = { }, 46062232e45SDan Williams [ND_CMD_ARS_CAP] = { 46162232e45SDan Williams .in_num = 2, 46262232e45SDan Williams .in_sizes = { 8, 8, }, 4634577b066SDan Williams .out_num = 4, 4644577b066SDan Williams .out_sizes = { 4, 4, 4, 4, }, 46562232e45SDan Williams }, 46662232e45SDan Williams [ND_CMD_ARS_START] = { 4674577b066SDan Williams .in_num = 5, 4684577b066SDan Williams .in_sizes = { 8, 8, 2, 1, 5, }, 4694577b066SDan Williams .out_num = 2, 4704577b066SDan Williams .out_sizes = { 4, 4, }, 47162232e45SDan Williams }, 47262232e45SDan Williams [ND_CMD_ARS_STATUS] = { 473747ffe11SDan Williams .out_num = 3, 474747ffe11SDan Williams .out_sizes = { 4, 4, UINT_MAX, }, 47562232e45SDan Williams }, 476d4f32367SDan Williams [ND_CMD_CLEAR_ERROR] = { 477d4f32367SDan Williams .in_num = 2, 478d4f32367SDan Williams .in_sizes = { 8, 8, }, 479d4f32367SDan Williams .out_num = 3, 480d4f32367SDan Williams .out_sizes = { 4, 4, 8, }, 481d4f32367SDan Williams }, 48231eca76bSDan Williams [ND_CMD_CALL] = { 48331eca76bSDan Williams .in_num = 2, 48431eca76bSDan Williams .in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, }, 48531eca76bSDan Williams .out_num = 1, 48631eca76bSDan Williams .out_sizes = { UINT_MAX, }, 48731eca76bSDan Williams }, 48862232e45SDan Williams }; 48962232e45SDan Williams 49062232e45SDan Williams const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) 49162232e45SDan Williams { 49262232e45SDan Williams if (cmd < ARRAY_SIZE(__nd_cmd_bus_descs)) 49362232e45SDan Williams return &__nd_cmd_bus_descs[cmd]; 49462232e45SDan Williams return NULL; 49562232e45SDan Williams } 49662232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_bus_desc); 49762232e45SDan Williams 49862232e45SDan Williams u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, 49962232e45SDan Williams const struct nd_cmd_desc *desc, int idx, void *buf) 50062232e45SDan Williams { 50162232e45SDan Williams if (idx >= desc->in_num) 50262232e45SDan Williams return UINT_MAX; 50362232e45SDan Williams 50462232e45SDan Williams if (desc->in_sizes[idx] < UINT_MAX) 50562232e45SDan Williams return desc->in_sizes[idx]; 50662232e45SDan Williams 50762232e45SDan Williams if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA && idx == 2) { 50862232e45SDan Williams struct nd_cmd_set_config_hdr *hdr = buf; 50962232e45SDan Williams 51062232e45SDan Williams return hdr->in_length; 51162232e45SDan Williams } else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) { 51262232e45SDan Williams struct nd_cmd_vendor_hdr *hdr = buf; 51362232e45SDan Williams 51462232e45SDan Williams return hdr->in_length; 51531eca76bSDan Williams } else if (cmd == ND_CMD_CALL) { 51631eca76bSDan Williams struct nd_cmd_pkg *pkg = buf; 51731eca76bSDan Williams 51831eca76bSDan Williams return pkg->nd_size_in; 51962232e45SDan Williams } 52062232e45SDan Williams 52162232e45SDan Williams return UINT_MAX; 52262232e45SDan Williams } 52362232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_in_size); 52462232e45SDan Williams 52562232e45SDan Williams u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd, 52662232e45SDan Williams const struct nd_cmd_desc *desc, int idx, const u32 *in_field, 52762232e45SDan Williams const u32 *out_field) 52862232e45SDan Williams { 52962232e45SDan Williams if (idx >= desc->out_num) 53062232e45SDan Williams return UINT_MAX; 53162232e45SDan Williams 53262232e45SDan Williams if (desc->out_sizes[idx] < UINT_MAX) 53362232e45SDan Williams return desc->out_sizes[idx]; 53462232e45SDan Williams 53562232e45SDan Williams if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && idx == 1) 53662232e45SDan Williams return in_field[1]; 53762232e45SDan Williams else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) 53862232e45SDan Williams return out_field[1]; 539747ffe11SDan Williams else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) 540747ffe11SDan Williams return out_field[1] - 8; 54131eca76bSDan Williams else if (cmd == ND_CMD_CALL) { 54231eca76bSDan Williams struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field; 54331eca76bSDan Williams 54431eca76bSDan Williams return pkg->nd_size_out; 54531eca76bSDan Williams } 54631eca76bSDan Williams 54762232e45SDan Williams 54862232e45SDan Williams return UINT_MAX; 54962232e45SDan Williams } 55062232e45SDan Williams EXPORT_SYMBOL_GPL(nd_cmd_out_size); 55162232e45SDan Williams 552bf9bccc1SDan Williams void wait_nvdimm_bus_probe_idle(struct device *dev) 553eaf96153SDan Williams { 554bf9bccc1SDan Williams struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 555bf9bccc1SDan Williams 556eaf96153SDan Williams do { 557eaf96153SDan Williams if (nvdimm_bus->probe_active == 0) 558eaf96153SDan Williams break; 559eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 560eaf96153SDan Williams wait_event(nvdimm_bus->probe_wait, 561eaf96153SDan Williams nvdimm_bus->probe_active == 0); 562eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 563eaf96153SDan Williams } while (true); 564eaf96153SDan Williams } 565eaf96153SDan Williams 566d4f32367SDan Williams static int pmem_active(struct device *dev, void *data) 567d4f32367SDan Williams { 568d4f32367SDan Williams if (is_nd_pmem(dev) && dev->driver) 569d4f32367SDan Williams return -EBUSY; 570d4f32367SDan Williams return 0; 571d4f32367SDan Williams } 572d4f32367SDan Williams 573eaf96153SDan Williams /* set_config requires an idle interleave set */ 57487bf572eSDan Williams static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, 57587bf572eSDan Williams struct nvdimm *nvdimm, unsigned int cmd) 576eaf96153SDan Williams { 57787bf572eSDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 57887bf572eSDan Williams 57987bf572eSDan Williams /* ask the bus provider if it would like to block this request */ 58087bf572eSDan Williams if (nd_desc->clear_to_send) { 58187bf572eSDan Williams int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd); 58287bf572eSDan Williams 58387bf572eSDan Williams if (rc) 58487bf572eSDan Williams return rc; 58587bf572eSDan Williams } 586eaf96153SDan Williams 587d4f32367SDan Williams /* require clear error to go through the pmem driver */ 588d4f32367SDan Williams if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR) 589d4f32367SDan Williams return device_for_each_child(&nvdimm_bus->dev, NULL, 590d4f32367SDan Williams pmem_active); 591d4f32367SDan Williams 592eaf96153SDan Williams if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) 593eaf96153SDan Williams return 0; 594eaf96153SDan Williams 59587bf572eSDan Williams /* prevent label manipulation while the kernel owns label updates */ 596bf9bccc1SDan Williams wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev); 597eaf96153SDan Williams if (atomic_read(&nvdimm->busy)) 598eaf96153SDan Williams return -EBUSY; 599eaf96153SDan Williams return 0; 600eaf96153SDan Williams } 601eaf96153SDan Williams 60262232e45SDan Williams static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 60362232e45SDan Williams int read_only, unsigned int ioctl_cmd, unsigned long arg) 60462232e45SDan Williams { 60562232e45SDan Williams struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 60662232e45SDan Williams size_t buf_len = 0, in_len = 0, out_len = 0; 60762232e45SDan Williams static char out_env[ND_CMD_MAX_ENVELOPE]; 60862232e45SDan Williams static char in_env[ND_CMD_MAX_ENVELOPE]; 60962232e45SDan Williams const struct nd_cmd_desc *desc = NULL; 61062232e45SDan Williams unsigned int cmd = _IOC_NR(ioctl_cmd); 61162232e45SDan Williams void __user *p = (void __user *) arg; 61262232e45SDan Williams struct device *dev = &nvdimm_bus->dev; 61331eca76bSDan Williams struct nd_cmd_pkg pkg; 61462232e45SDan Williams const char *cmd_name, *dimm_name; 615e3654ecaSDan Williams unsigned long cmd_mask; 61662232e45SDan Williams void *buf; 61762232e45SDan Williams int rc, i; 61862232e45SDan Williams 61962232e45SDan Williams if (nvdimm) { 62062232e45SDan Williams desc = nd_cmd_dimm_desc(cmd); 62162232e45SDan Williams cmd_name = nvdimm_cmd_name(cmd); 622e3654ecaSDan Williams cmd_mask = nvdimm->cmd_mask; 62362232e45SDan Williams dimm_name = dev_name(&nvdimm->dev); 62462232e45SDan Williams } else { 62562232e45SDan Williams desc = nd_cmd_bus_desc(cmd); 62662232e45SDan Williams cmd_name = nvdimm_bus_cmd_name(cmd); 627e3654ecaSDan Williams cmd_mask = nd_desc->cmd_mask; 62862232e45SDan Williams dimm_name = "bus"; 62962232e45SDan Williams } 63062232e45SDan Williams 63131eca76bSDan Williams if (cmd == ND_CMD_CALL) { 63231eca76bSDan Williams if (copy_from_user(&pkg, p, sizeof(pkg))) 63331eca76bSDan Williams return -EFAULT; 63431eca76bSDan Williams } 63531eca76bSDan Williams 63662232e45SDan Williams if (!desc || (desc->out_num + desc->in_num == 0) || 637e3654ecaSDan Williams !test_bit(cmd, &cmd_mask)) 63862232e45SDan Williams return -ENOTTY; 63962232e45SDan Williams 64062232e45SDan Williams /* fail write commands (when read-only) */ 64162232e45SDan Williams if (read_only) 64207accfa9SJerry Hoemann switch (cmd) { 64307accfa9SJerry Hoemann case ND_CMD_VENDOR: 64407accfa9SJerry Hoemann case ND_CMD_SET_CONFIG_DATA: 64507accfa9SJerry Hoemann case ND_CMD_ARS_START: 646d4f32367SDan Williams case ND_CMD_CLEAR_ERROR: 64731eca76bSDan Williams case ND_CMD_CALL: 64862232e45SDan Williams dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 64962232e45SDan Williams nvdimm ? nvdimm_cmd_name(cmd) 65062232e45SDan Williams : nvdimm_bus_cmd_name(cmd)); 65162232e45SDan Williams return -EPERM; 65262232e45SDan Williams default: 65362232e45SDan Williams break; 65462232e45SDan Williams } 65562232e45SDan Williams 65662232e45SDan Williams /* process an input envelope */ 65762232e45SDan Williams for (i = 0; i < desc->in_num; i++) { 65862232e45SDan Williams u32 in_size, copy; 65962232e45SDan Williams 66062232e45SDan Williams in_size = nd_cmd_in_size(nvdimm, cmd, desc, i, in_env); 66162232e45SDan Williams if (in_size == UINT_MAX) { 66262232e45SDan Williams dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n", 66362232e45SDan Williams __func__, dimm_name, cmd_name, i); 66462232e45SDan Williams return -ENXIO; 66562232e45SDan Williams } 66662232e45SDan Williams if (in_len < sizeof(in_env)) 66762232e45SDan Williams copy = min_t(u32, sizeof(in_env) - in_len, in_size); 66862232e45SDan Williams else 66962232e45SDan Williams copy = 0; 67062232e45SDan Williams if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) 67162232e45SDan Williams return -EFAULT; 67262232e45SDan Williams in_len += in_size; 67362232e45SDan Williams } 67462232e45SDan Williams 67531eca76bSDan Williams if (cmd == ND_CMD_CALL) { 67631eca76bSDan Williams dev_dbg(dev, "%s:%s, idx: %llu, in: %zu, out: %zu, len %zu\n", 67731eca76bSDan Williams __func__, dimm_name, pkg.nd_command, 67831eca76bSDan Williams in_len, out_len, buf_len); 67931eca76bSDan Williams 68031eca76bSDan Williams for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++) 68131eca76bSDan Williams if (pkg.nd_reserved2[i]) 68231eca76bSDan Williams return -EINVAL; 68331eca76bSDan Williams } 68431eca76bSDan Williams 68562232e45SDan Williams /* process an output envelope */ 68662232e45SDan Williams for (i = 0; i < desc->out_num; i++) { 68762232e45SDan Williams u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, 68862232e45SDan Williams (u32 *) in_env, (u32 *) out_env); 68962232e45SDan Williams u32 copy; 69062232e45SDan Williams 69162232e45SDan Williams if (out_size == UINT_MAX) { 69262232e45SDan Williams dev_dbg(dev, "%s:%s unknown output size cmd: %s field: %d\n", 69362232e45SDan Williams __func__, dimm_name, cmd_name, i); 69462232e45SDan Williams return -EFAULT; 69562232e45SDan Williams } 69662232e45SDan Williams if (out_len < sizeof(out_env)) 69762232e45SDan Williams copy = min_t(u32, sizeof(out_env) - out_len, out_size); 69862232e45SDan Williams else 69962232e45SDan Williams copy = 0; 70062232e45SDan Williams if (copy && copy_from_user(&out_env[out_len], 70162232e45SDan Williams p + in_len + out_len, copy)) 70262232e45SDan Williams return -EFAULT; 70362232e45SDan Williams out_len += out_size; 70462232e45SDan Williams } 70562232e45SDan Williams 70662232e45SDan Williams buf_len = out_len + in_len; 70762232e45SDan Williams if (buf_len > ND_IOCTL_MAX_BUFLEN) { 70862232e45SDan Williams dev_dbg(dev, "%s:%s cmd: %s buf_len: %zu > %d\n", __func__, 70962232e45SDan Williams dimm_name, cmd_name, buf_len, 71062232e45SDan Williams ND_IOCTL_MAX_BUFLEN); 71162232e45SDan Williams return -EINVAL; 71262232e45SDan Williams } 71362232e45SDan Williams 71462232e45SDan Williams buf = vmalloc(buf_len); 71562232e45SDan Williams if (!buf) 71662232e45SDan Williams return -ENOMEM; 71762232e45SDan Williams 71862232e45SDan Williams if (copy_from_user(buf, p, buf_len)) { 71962232e45SDan Williams rc = -EFAULT; 72062232e45SDan Williams goto out; 72162232e45SDan Williams } 72262232e45SDan Williams 723eaf96153SDan Williams nvdimm_bus_lock(&nvdimm_bus->dev); 72487bf572eSDan Williams rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, cmd); 725eaf96153SDan Williams if (rc) 726eaf96153SDan Williams goto out_unlock; 727eaf96153SDan Williams 728aef25338SDan Williams rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, NULL); 72962232e45SDan Williams if (rc < 0) 730eaf96153SDan Williams goto out_unlock; 73162232e45SDan Williams if (copy_to_user(p, buf, buf_len)) 73262232e45SDan Williams rc = -EFAULT; 733eaf96153SDan Williams out_unlock: 734eaf96153SDan Williams nvdimm_bus_unlock(&nvdimm_bus->dev); 73562232e45SDan Williams out: 73662232e45SDan Williams vfree(buf); 73762232e45SDan Williams return rc; 73862232e45SDan Williams } 73962232e45SDan Williams 74045def22cSDan Williams static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 74145def22cSDan Williams { 74262232e45SDan Williams long id = (long) file->private_data; 7434dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 74462232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 74562232e45SDan Williams 7464dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 74762232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 74862232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 74962232e45SDan Williams if (nvdimm_bus->id == id) { 7504dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, NULL, ro, cmd, arg); 75162232e45SDan Williams break; 75262232e45SDan Williams } 75362232e45SDan Williams } 75462232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 75562232e45SDan Williams 75662232e45SDan Williams return rc; 75762232e45SDan Williams } 75862232e45SDan Williams 75962232e45SDan Williams static int match_dimm(struct device *dev, void *data) 76062232e45SDan Williams { 76162232e45SDan Williams long id = (long) data; 76262232e45SDan Williams 76362232e45SDan Williams if (is_nvdimm(dev)) { 76462232e45SDan Williams struct nvdimm *nvdimm = to_nvdimm(dev); 76562232e45SDan Williams 76662232e45SDan Williams return nvdimm->id == id; 76762232e45SDan Williams } 76862232e45SDan Williams 76962232e45SDan Williams return 0; 77062232e45SDan Williams } 77162232e45SDan Williams 77262232e45SDan Williams static long nvdimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 77362232e45SDan Williams { 7744dc0e7beSJerry Hoemann int rc = -ENXIO, ro; 77562232e45SDan Williams struct nvdimm_bus *nvdimm_bus; 77662232e45SDan Williams 7774dc0e7beSJerry Hoemann ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); 77862232e45SDan Williams mutex_lock(&nvdimm_bus_list_mutex); 77962232e45SDan Williams list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { 78062232e45SDan Williams struct device *dev = device_find_child(&nvdimm_bus->dev, 78162232e45SDan Williams file->private_data, match_dimm); 78262232e45SDan Williams struct nvdimm *nvdimm; 78362232e45SDan Williams 78462232e45SDan Williams if (!dev) 78562232e45SDan Williams continue; 78662232e45SDan Williams 78762232e45SDan Williams nvdimm = to_nvdimm(dev); 7884dc0e7beSJerry Hoemann rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg); 78962232e45SDan Williams put_device(dev); 79062232e45SDan Williams break; 79162232e45SDan Williams } 79262232e45SDan Williams mutex_unlock(&nvdimm_bus_list_mutex); 79362232e45SDan Williams 79462232e45SDan Williams return rc; 79562232e45SDan Williams } 79662232e45SDan Williams 79762232e45SDan Williams static int nd_open(struct inode *inode, struct file *file) 79862232e45SDan Williams { 79962232e45SDan Williams long minor = iminor(inode); 80062232e45SDan Williams 80162232e45SDan Williams file->private_data = (void *) minor; 80262232e45SDan Williams return 0; 80345def22cSDan Williams } 80445def22cSDan Williams 80545def22cSDan Williams static const struct file_operations nvdimm_bus_fops = { 80645def22cSDan Williams .owner = THIS_MODULE, 80762232e45SDan Williams .open = nd_open, 80845def22cSDan Williams .unlocked_ioctl = nd_ioctl, 80945def22cSDan Williams .compat_ioctl = nd_ioctl, 81045def22cSDan Williams .llseek = noop_llseek, 81145def22cSDan Williams }; 81245def22cSDan Williams 81362232e45SDan Williams static const struct file_operations nvdimm_fops = { 81462232e45SDan Williams .owner = THIS_MODULE, 81562232e45SDan Williams .open = nd_open, 81662232e45SDan Williams .unlocked_ioctl = nvdimm_ioctl, 81762232e45SDan Williams .compat_ioctl = nvdimm_ioctl, 81862232e45SDan Williams .llseek = noop_llseek, 81962232e45SDan Williams }; 82062232e45SDan Williams 82145def22cSDan Williams int __init nvdimm_bus_init(void) 82245def22cSDan Williams { 82345def22cSDan Williams int rc; 82445def22cSDan Williams 825e6dfb2deSDan Williams rc = bus_register(&nvdimm_bus_type); 826e6dfb2deSDan Williams if (rc) 827e6dfb2deSDan Williams return rc; 828e6dfb2deSDan Williams 82945def22cSDan Williams rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops); 83045def22cSDan Williams if (rc < 0) 83162232e45SDan Williams goto err_bus_chrdev; 83245def22cSDan Williams nvdimm_bus_major = rc; 83345def22cSDan Williams 83462232e45SDan Williams rc = register_chrdev(0, "dimmctl", &nvdimm_fops); 83562232e45SDan Williams if (rc < 0) 83662232e45SDan Williams goto err_dimm_chrdev; 83762232e45SDan Williams nvdimm_major = rc; 83862232e45SDan Williams 83945def22cSDan Williams nd_class = class_create(THIS_MODULE, "nd"); 840daa1dee4SAxel Lin if (IS_ERR(nd_class)) { 841daa1dee4SAxel Lin rc = PTR_ERR(nd_class); 84245def22cSDan Williams goto err_class; 843daa1dee4SAxel Lin } 84445def22cSDan Williams 84545def22cSDan Williams return 0; 84645def22cSDan Williams 84745def22cSDan Williams err_class: 84862232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 84962232e45SDan Williams err_dimm_chrdev: 85045def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 85162232e45SDan Williams err_bus_chrdev: 852e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 85345def22cSDan Williams 85445def22cSDan Williams return rc; 85545def22cSDan Williams } 85645def22cSDan Williams 8574d88a97aSDan Williams void nvdimm_bus_exit(void) 85845def22cSDan Williams { 85945def22cSDan Williams class_destroy(nd_class); 86045def22cSDan Williams unregister_chrdev(nvdimm_bus_major, "ndctl"); 86162232e45SDan Williams unregister_chrdev(nvdimm_major, "dimmctl"); 862e6dfb2deSDan Williams bus_unregister(&nvdimm_bus_type); 86345def22cSDan Williams } 864