xref: /openbmc/linux/drivers/nvdimm/region.c (revision cd03412a)
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 {
21047fc8a1SRoss Zwisler 	int err, rc;
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 
265212e11fSVishal Verma 	if (nd_region->num_lanes > num_online_cpus()
275212e11fSVishal Verma 			&& nd_region->num_lanes < num_possible_cpus()
285212e11fSVishal Verma 			&& !test_and_set_bit(0, &once)) {
295212e11fSVishal Verma 		dev_info(dev, "online cpus (%d) < concurrent i/o lanes (%d) < possible cpus (%d)\n",
305212e11fSVishal Verma 				num_online_cpus(), nd_region->num_lanes,
315212e11fSVishal Verma 				num_possible_cpus());
325212e11fSVishal Verma 		dev_info(dev, "setting nr_cpus=%d may yield better libnvdimm device performance\n",
335212e11fSVishal Verma 				nd_region->num_lanes);
345212e11fSVishal Verma 	}
355212e11fSVishal Verma 
36047fc8a1SRoss Zwisler 	rc = nd_blk_region_init(nd_region);
37047fc8a1SRoss Zwisler 	if (rc)
38047fc8a1SRoss Zwisler 		return rc;
39047fc8a1SRoss Zwisler 
40047fc8a1SRoss Zwisler 	rc = nd_region_register_namespaces(nd_region, &err);
413d88002eSDan Williams 	num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL);
423d88002eSDan Williams 	if (!num_ns)
433d88002eSDan Williams 		return -ENOMEM;
443d88002eSDan Williams 
453d88002eSDan Williams 	if (rc < 0)
463d88002eSDan Williams 		return rc;
473d88002eSDan Williams 
483d88002eSDan Williams 	num_ns->active = rc;
493d88002eSDan Williams 	num_ns->count = rc + err;
503d88002eSDan Williams 	dev_set_drvdata(dev, num_ns);
513d88002eSDan Williams 
528c2f7e86SDan Williams 	if (rc && err && rc == err)
538c2f7e86SDan Williams 		return -ENODEV;
548c2f7e86SDan Williams 
558c2f7e86SDan Williams 	nd_region->btt_seed = nd_btt_create(nd_region);
56e1455744SDan Williams 	nd_region->pfn_seed = nd_pfn_create(nd_region);
57cd03412aSDan Williams 	nd_region->dax_seed = nd_dax_create(nd_region);
583d88002eSDan Williams 	if (err == 0)
593d88002eSDan Williams 		return 0;
603d88002eSDan Williams 
613d88002eSDan Williams 	/*
623d88002eSDan Williams 	 * Given multiple namespaces per region, we do not want to
633d88002eSDan Williams 	 * disable all the successfully registered peer namespaces upon
643d88002eSDan Williams 	 * a single registration failure.  If userspace is missing a
653d88002eSDan Williams 	 * namespace that it expects it can disable/re-enable the region
663d88002eSDan Williams 	 * to retry discovery after correcting the failure.
673d88002eSDan Williams 	 * <regionX>/namespaces returns the current
683d88002eSDan Williams 	 * "<async-registered>/<total>" namespace count.
693d88002eSDan Williams 	 */
703d88002eSDan Williams 	dev_err(dev, "failed to register %d namespace%s, continuing...\n",
713d88002eSDan Williams 			err, err == 1 ? "" : "s");
723d88002eSDan Williams 	return 0;
733d88002eSDan Williams }
743d88002eSDan Williams 
753d88002eSDan Williams static int child_unregister(struct device *dev, void *data)
763d88002eSDan Williams {
773d88002eSDan Williams 	nd_device_unregister(dev, ND_SYNC);
783d88002eSDan Williams 	return 0;
793d88002eSDan Williams }
803d88002eSDan Williams 
813d88002eSDan Williams static int nd_region_remove(struct device *dev)
823d88002eSDan Williams {
83bf9bccc1SDan Williams 	struct nd_region *nd_region = to_nd_region(dev);
84bf9bccc1SDan Williams 
853d88002eSDan Williams 	/* flush attribute readers and disable */
863d88002eSDan Williams 	nvdimm_bus_lock(dev);
87bf9bccc1SDan Williams 	nd_region->ns_seed = NULL;
888c2f7e86SDan Williams 	nd_region->btt_seed = NULL;
89e1455744SDan Williams 	nd_region->pfn_seed = NULL;
90cd03412aSDan Williams 	nd_region->dax_seed = NULL;
913d88002eSDan Williams 	dev_set_drvdata(dev, NULL);
923d88002eSDan Williams 	nvdimm_bus_unlock(dev);
933d88002eSDan Williams 
943d88002eSDan Williams 	device_for_each_child(dev, NULL, child_unregister);
953d88002eSDan Williams 	return 0;
963d88002eSDan Williams }
973d88002eSDan Williams 
9871999466SDan Williams static int child_notify(struct device *dev, void *data)
9971999466SDan Williams {
10071999466SDan Williams 	nd_device_notify(dev, *(enum nvdimm_event *) data);
10171999466SDan Williams 	return 0;
10271999466SDan Williams }
10371999466SDan Williams 
10471999466SDan Williams static void nd_region_notify(struct device *dev, enum nvdimm_event event)
10571999466SDan Williams {
10671999466SDan Williams 	device_for_each_child(dev, &event, child_notify);
10771999466SDan Williams }
10871999466SDan Williams 
1093d88002eSDan Williams static struct nd_device_driver nd_region_driver = {
1103d88002eSDan Williams 	.probe = nd_region_probe,
1113d88002eSDan Williams 	.remove = nd_region_remove,
11271999466SDan Williams 	.notify = nd_region_notify,
1133d88002eSDan Williams 	.drv = {
1143d88002eSDan Williams 		.name = "nd_region",
1153d88002eSDan Williams 	},
1163d88002eSDan Williams 	.type = ND_DRIVER_REGION_BLK | ND_DRIVER_REGION_PMEM,
1173d88002eSDan Williams };
1183d88002eSDan Williams 
1193d88002eSDan Williams int __init nd_region_init(void)
1203d88002eSDan Williams {
1213d88002eSDan Williams 	return nd_driver_register(&nd_region_driver);
1223d88002eSDan Williams }
1233d88002eSDan Williams 
1243d88002eSDan Williams void nd_region_exit(void)
1253d88002eSDan Williams {
1263d88002eSDan Williams 	driver_unregister(&nd_region_driver.drv);
1273d88002eSDan Williams }
1283d88002eSDan Williams 
1293d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_PMEM);
1303d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_BLK);
131