xref: /openbmc/linux/drivers/nvdimm/region.c (revision 5212e11f)
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