13d88002eSDan Williams /* 23d88002eSDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 33d88002eSDan Williams * 43d88002eSDan Williams * This program is free software; you can redistribute it and/or modify 53d88002eSDan Williams * it under the terms of version 2 of the GNU General Public License as 63d88002eSDan Williams * published by the Free Software Foundation. 73d88002eSDan Williams * 83d88002eSDan Williams * This program is distributed in the hope that it will be useful, but 93d88002eSDan Williams * WITHOUT ANY WARRANTY; without even the implied warranty of 103d88002eSDan Williams * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 113d88002eSDan Williams * General Public License for more details. 123d88002eSDan Williams */ 135212e11fSVishal Verma #include <linux/cpumask.h> 143d88002eSDan Williams #include <linux/module.h> 153d88002eSDan Williams #include <linux/device.h> 163d88002eSDan Williams #include <linux/nd.h> 173d88002eSDan Williams #include "nd.h" 183d88002eSDan Williams 193d88002eSDan Williams static int nd_region_probe(struct device *dev) 203d88002eSDan Williams { 213d88002eSDan Williams int err; 225212e11fSVishal Verma static unsigned long once; 233d88002eSDan Williams struct nd_region_namespaces *num_ns; 243d88002eSDan Williams struct nd_region *nd_region = to_nd_region(dev); 253d88002eSDan Williams int rc = nd_region_register_namespaces(nd_region, &err); 263d88002eSDan Williams 275212e11fSVishal Verma if (nd_region->num_lanes > num_online_cpus() 285212e11fSVishal Verma && nd_region->num_lanes < num_possible_cpus() 295212e11fSVishal Verma && !test_and_set_bit(0, &once)) { 305212e11fSVishal Verma dev_info(dev, "online cpus (%d) < concurrent i/o lanes (%d) < possible cpus (%d)\n", 315212e11fSVishal Verma num_online_cpus(), nd_region->num_lanes, 325212e11fSVishal Verma num_possible_cpus()); 335212e11fSVishal Verma dev_info(dev, "setting nr_cpus=%d may yield better libnvdimm device performance\n", 345212e11fSVishal Verma nd_region->num_lanes); 355212e11fSVishal Verma } 365212e11fSVishal Verma 373d88002eSDan Williams num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL); 383d88002eSDan Williams if (!num_ns) 393d88002eSDan Williams return -ENOMEM; 403d88002eSDan Williams 413d88002eSDan Williams if (rc < 0) 423d88002eSDan Williams return rc; 433d88002eSDan Williams 443d88002eSDan Williams num_ns->active = rc; 453d88002eSDan Williams num_ns->count = rc + err; 463d88002eSDan Williams dev_set_drvdata(dev, num_ns); 473d88002eSDan Williams 488c2f7e86SDan Williams if (rc && err && rc == err) 498c2f7e86SDan Williams return -ENODEV; 508c2f7e86SDan Williams 518c2f7e86SDan Williams nd_region->btt_seed = nd_btt_create(nd_region); 523d88002eSDan Williams if (err == 0) 533d88002eSDan Williams return 0; 543d88002eSDan Williams 553d88002eSDan Williams /* 563d88002eSDan Williams * Given multiple namespaces per region, we do not want to 573d88002eSDan Williams * disable all the successfully registered peer namespaces upon 583d88002eSDan Williams * a single registration failure. If userspace is missing a 593d88002eSDan Williams * namespace that it expects it can disable/re-enable the region 603d88002eSDan Williams * to retry discovery after correcting the failure. 613d88002eSDan Williams * <regionX>/namespaces returns the current 623d88002eSDan Williams * "<async-registered>/<total>" namespace count. 633d88002eSDan Williams */ 643d88002eSDan Williams dev_err(dev, "failed to register %d namespace%s, continuing...\n", 653d88002eSDan Williams err, err == 1 ? "" : "s"); 663d88002eSDan Williams return 0; 673d88002eSDan Williams } 683d88002eSDan Williams 693d88002eSDan Williams static int child_unregister(struct device *dev, void *data) 703d88002eSDan Williams { 713d88002eSDan Williams nd_device_unregister(dev, ND_SYNC); 723d88002eSDan Williams return 0; 733d88002eSDan Williams } 743d88002eSDan Williams 753d88002eSDan Williams static int nd_region_remove(struct device *dev) 763d88002eSDan Williams { 77bf9bccc1SDan Williams struct nd_region *nd_region = to_nd_region(dev); 78bf9bccc1SDan Williams 793d88002eSDan Williams /* flush attribute readers and disable */ 803d88002eSDan Williams nvdimm_bus_lock(dev); 81bf9bccc1SDan Williams nd_region->ns_seed = NULL; 828c2f7e86SDan Williams nd_region->btt_seed = NULL; 833d88002eSDan Williams dev_set_drvdata(dev, NULL); 843d88002eSDan Williams nvdimm_bus_unlock(dev); 853d88002eSDan Williams 863d88002eSDan Williams device_for_each_child(dev, NULL, child_unregister); 873d88002eSDan Williams return 0; 883d88002eSDan Williams } 893d88002eSDan Williams 903d88002eSDan Williams static struct nd_device_driver nd_region_driver = { 913d88002eSDan Williams .probe = nd_region_probe, 923d88002eSDan Williams .remove = nd_region_remove, 933d88002eSDan Williams .drv = { 943d88002eSDan Williams .name = "nd_region", 953d88002eSDan Williams }, 963d88002eSDan Williams .type = ND_DRIVER_REGION_BLK | ND_DRIVER_REGION_PMEM, 973d88002eSDan Williams }; 983d88002eSDan Williams 993d88002eSDan Williams int __init nd_region_init(void) 1003d88002eSDan Williams { 1013d88002eSDan Williams return nd_driver_register(&nd_region_driver); 1023d88002eSDan Williams } 1033d88002eSDan Williams 1043d88002eSDan Williams void nd_region_exit(void) 1053d88002eSDan Williams { 1063d88002eSDan Williams driver_unregister(&nd_region_driver.drv); 1073d88002eSDan Williams } 1083d88002eSDan Williams 1093d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_PMEM); 1103d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_BLK); 111