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 */ 133d88002eSDan Williams #include <linux/module.h> 143d88002eSDan Williams #include <linux/device.h> 153d88002eSDan Williams #include <linux/nd.h> 163d88002eSDan Williams #include "nd.h" 173d88002eSDan Williams 183d88002eSDan Williams static int nd_region_probe(struct device *dev) 193d88002eSDan Williams { 203d88002eSDan Williams int err; 213d88002eSDan Williams struct nd_region_namespaces *num_ns; 223d88002eSDan Williams struct nd_region *nd_region = to_nd_region(dev); 233d88002eSDan Williams int rc = nd_region_register_namespaces(nd_region, &err); 243d88002eSDan Williams 253d88002eSDan Williams num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL); 263d88002eSDan Williams if (!num_ns) 273d88002eSDan Williams return -ENOMEM; 283d88002eSDan Williams 293d88002eSDan Williams if (rc < 0) 303d88002eSDan Williams return rc; 313d88002eSDan Williams 323d88002eSDan Williams num_ns->active = rc; 333d88002eSDan Williams num_ns->count = rc + err; 343d88002eSDan Williams dev_set_drvdata(dev, num_ns); 353d88002eSDan Williams 368c2f7e86SDan Williams if (rc && err && rc == err) 378c2f7e86SDan Williams return -ENODEV; 388c2f7e86SDan Williams 398c2f7e86SDan Williams nd_region->btt_seed = nd_btt_create(nd_region); 403d88002eSDan Williams if (err == 0) 413d88002eSDan Williams return 0; 423d88002eSDan Williams 433d88002eSDan Williams /* 443d88002eSDan Williams * Given multiple namespaces per region, we do not want to 453d88002eSDan Williams * disable all the successfully registered peer namespaces upon 463d88002eSDan Williams * a single registration failure. If userspace is missing a 473d88002eSDan Williams * namespace that it expects it can disable/re-enable the region 483d88002eSDan Williams * to retry discovery after correcting the failure. 493d88002eSDan Williams * <regionX>/namespaces returns the current 503d88002eSDan Williams * "<async-registered>/<total>" namespace count. 513d88002eSDan Williams */ 523d88002eSDan Williams dev_err(dev, "failed to register %d namespace%s, continuing...\n", 533d88002eSDan Williams err, err == 1 ? "" : "s"); 543d88002eSDan Williams return 0; 553d88002eSDan Williams } 563d88002eSDan Williams 573d88002eSDan Williams static int child_unregister(struct device *dev, void *data) 583d88002eSDan Williams { 593d88002eSDan Williams nd_device_unregister(dev, ND_SYNC); 603d88002eSDan Williams return 0; 613d88002eSDan Williams } 623d88002eSDan Williams 633d88002eSDan Williams static int nd_region_remove(struct device *dev) 643d88002eSDan Williams { 65bf9bccc1SDan Williams struct nd_region *nd_region = to_nd_region(dev); 66bf9bccc1SDan Williams 673d88002eSDan Williams /* flush attribute readers and disable */ 683d88002eSDan Williams nvdimm_bus_lock(dev); 69bf9bccc1SDan Williams nd_region->ns_seed = NULL; 708c2f7e86SDan Williams nd_region->btt_seed = NULL; 713d88002eSDan Williams dev_set_drvdata(dev, NULL); 723d88002eSDan Williams nvdimm_bus_unlock(dev); 733d88002eSDan Williams 743d88002eSDan Williams device_for_each_child(dev, NULL, child_unregister); 753d88002eSDan Williams return 0; 763d88002eSDan Williams } 773d88002eSDan Williams 783d88002eSDan Williams static struct nd_device_driver nd_region_driver = { 793d88002eSDan Williams .probe = nd_region_probe, 803d88002eSDan Williams .remove = nd_region_remove, 813d88002eSDan Williams .drv = { 823d88002eSDan Williams .name = "nd_region", 833d88002eSDan Williams }, 843d88002eSDan Williams .type = ND_DRIVER_REGION_BLK | ND_DRIVER_REGION_PMEM, 853d88002eSDan Williams }; 863d88002eSDan Williams 873d88002eSDan Williams int __init nd_region_init(void) 883d88002eSDan Williams { 893d88002eSDan Williams return nd_driver_register(&nd_region_driver); 903d88002eSDan Williams } 913d88002eSDan Williams 923d88002eSDan Williams void nd_region_exit(void) 933d88002eSDan Williams { 943d88002eSDan Williams driver_unregister(&nd_region_driver.drv); 953d88002eSDan Williams } 963d88002eSDan Williams 973d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_PMEM); 983d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_BLK); 99