xref: /openbmc/linux/drivers/nvdimm/region.c (revision dc370b28)
15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23d88002eSDan Williams /*
33d88002eSDan Williams  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
43d88002eSDan Williams  */
5*dc370b28SDan Williams #include <linux/memregion.h>
65212e11fSVishal Verma #include <linux/cpumask.h>
73d88002eSDan Williams #include <linux/module.h>
83d88002eSDan Williams #include <linux/device.h>
93d88002eSDan Williams #include <linux/nd.h>
106a6bef90SDave Jiang #include "nd-core.h"
113d88002eSDan Williams #include "nd.h"
123d88002eSDan Williams 
nd_region_probe(struct device * dev)133d88002eSDan Williams static int nd_region_probe(struct device *dev)
143d88002eSDan Williams {
15047fc8a1SRoss Zwisler 	int err, rc;
165212e11fSVishal Verma 	static unsigned long once;
17e5ae3b25SDan Williams 	struct nd_region_data *ndrd;
183d88002eSDan Williams 	struct nd_region *nd_region = to_nd_region(dev);
193b6c6c03SDan Williams 	struct range range = {
203b6c6c03SDan Williams 		.start = nd_region->ndr_start,
213b6c6c03SDan Williams 		.end = nd_region->ndr_start + nd_region->ndr_size - 1,
223b6c6c03SDan Williams 	};
233d88002eSDan Williams 
245212e11fSVishal Verma 	if (nd_region->num_lanes > num_online_cpus()
255212e11fSVishal Verma 			&& nd_region->num_lanes < num_possible_cpus()
265212e11fSVishal Verma 			&& !test_and_set_bit(0, &once)) {
2760ce0f93SDan Williams 		dev_dbg(dev, "online cpus (%d) < concurrent i/o lanes (%d) < possible cpus (%d)\n",
285212e11fSVishal Verma 				num_online_cpus(), nd_region->num_lanes,
295212e11fSVishal Verma 				num_possible_cpus());
3060ce0f93SDan Williams 		dev_dbg(dev, "setting nr_cpus=%d may yield better libnvdimm device performance\n",
315212e11fSVishal Verma 				nd_region->num_lanes);
325212e11fSVishal Verma 	}
335212e11fSVishal Verma 
34e5ae3b25SDan Williams 	rc = nd_region_activate(nd_region);
35e5ae3b25SDan Williams 	if (rc)
36e5ae3b25SDan Williams 		return rc;
37e5ae3b25SDan Williams 
386a6bef90SDave Jiang 	if (devm_init_badblocks(dev, &nd_region->bb))
396a6bef90SDave Jiang 		return -ENODEV;
403b6c6c03SDan Williams 	nd_region->bb_state =
413b6c6c03SDan Williams 		sysfs_get_dirent(nd_region->dev.kobj.sd, "badblocks");
426aa734a2SDan Williams 	if (!nd_region->bb_state)
433b6c6c03SDan Williams 		dev_warn(dev, "'badblocks' notification disabled\n");
44a4574f63SDan Williams 	nvdimm_badblocks_populate(nd_region, &nd_region->bb, &range);
456a6bef90SDave Jiang 
46700cd033SDan Williams 	rc = nd_region_register_namespaces(nd_region, &err);
47700cd033SDan Williams 	if (rc < 0)
48700cd033SDan Williams 		return rc;
49700cd033SDan Williams 
50700cd033SDan Williams 	ndrd = dev_get_drvdata(dev);
51700cd033SDan Williams 	ndrd->ns_active = rc;
52700cd033SDan Williams 	ndrd->ns_count = rc + err;
53700cd033SDan Williams 
54700cd033SDan Williams 	if (rc && err && rc == err)
55700cd033SDan Williams 		return -ENODEV;
56700cd033SDan Williams 
578c2f7e86SDan Williams 	nd_region->btt_seed = nd_btt_create(nd_region);
58e1455744SDan Williams 	nd_region->pfn_seed = nd_pfn_create(nd_region);
59cd03412aSDan Williams 	nd_region->dax_seed = nd_dax_create(nd_region);
603d88002eSDan Williams 	if (err == 0)
613d88002eSDan Williams 		return 0;
623d88002eSDan Williams 
633d88002eSDan Williams 	/*
643d88002eSDan Williams 	 * Given multiple namespaces per region, we do not want to
653d88002eSDan Williams 	 * disable all the successfully registered peer namespaces upon
663d88002eSDan Williams 	 * a single registration failure.  If userspace is missing a
673d88002eSDan Williams 	 * namespace that it expects it can disable/re-enable the region
683d88002eSDan Williams 	 * to retry discovery after correcting the failure.
693d88002eSDan Williams 	 * <regionX>/namespaces returns the current
703d88002eSDan Williams 	 * "<async-registered>/<total>" namespace count.
713d88002eSDan Williams 	 */
723d88002eSDan Williams 	dev_err(dev, "failed to register %d namespace%s, continuing...\n",
733d88002eSDan Williams 			err, err == 1 ? "" : "s");
743d88002eSDan Williams 	return 0;
753d88002eSDan Williams }
763d88002eSDan Williams 
child_unregister(struct device * dev,void * data)773d88002eSDan Williams static int child_unregister(struct device *dev, void *data)
783d88002eSDan Williams {
793d88002eSDan Williams 	nd_device_unregister(dev, ND_SYNC);
803d88002eSDan Williams 	return 0;
813d88002eSDan Williams }
823d88002eSDan Williams 
nd_region_remove(struct device * dev)831f975074SUwe Kleine-König static void nd_region_remove(struct device *dev)
843d88002eSDan Williams {
85bf9bccc1SDan Williams 	struct nd_region *nd_region = to_nd_region(dev);
86bf9bccc1SDan Williams 
87a8f72022SDan Williams 	device_for_each_child(dev, NULL, child_unregister);
88a8f72022SDan Williams 
893d88002eSDan Williams 	/* flush attribute readers and disable */
903d88002eSDan Williams 	nvdimm_bus_lock(dev);
91bf9bccc1SDan Williams 	nd_region->ns_seed = NULL;
928c2f7e86SDan Williams 	nd_region->btt_seed = NULL;
93e1455744SDan Williams 	nd_region->pfn_seed = NULL;
94cd03412aSDan Williams 	nd_region->dax_seed = NULL;
953d88002eSDan Williams 	dev_set_drvdata(dev, NULL);
963d88002eSDan Williams 	nvdimm_bus_unlock(dev);
973d88002eSDan Williams 
986aa734a2SDan Williams 	/*
9981beea55SDan Williams 	 * Note, this assumes device_lock() context to not race
1006aa734a2SDan Williams 	 * nd_region_notify()
1016aa734a2SDan Williams 	 */
1026aa734a2SDan Williams 	sysfs_put(nd_region->bb_state);
1036aa734a2SDan Williams 	nd_region->bb_state = NULL;
104*dc370b28SDan Williams 
105*dc370b28SDan Williams 	/*
106*dc370b28SDan Williams 	 * Try to flush caches here since a disabled region may be subject to
107*dc370b28SDan Williams 	 * secure erase while disabled, and previous dirty data should not be
108*dc370b28SDan Williams 	 * written back to a new instance of the region. This only matters on
109*dc370b28SDan Williams 	 * bare metal where security commands are available, so silent failure
110*dc370b28SDan Williams 	 * here is ok.
111*dc370b28SDan Williams 	 */
112*dc370b28SDan Williams 	if (cpu_cache_has_invalidate_memregion())
113*dc370b28SDan Williams 		cpu_cache_invalidate_memregion(IORES_DESC_PERSISTENT_MEMORY);
1143d88002eSDan Williams }
1153d88002eSDan Williams 
child_notify(struct device * dev,void * data)11671999466SDan Williams static int child_notify(struct device *dev, void *data)
11771999466SDan Williams {
11871999466SDan Williams 	nd_device_notify(dev, *(enum nvdimm_event *) data);
11971999466SDan Williams 	return 0;
12071999466SDan Williams }
12171999466SDan Williams 
nd_region_notify(struct device * dev,enum nvdimm_event event)12271999466SDan Williams static void nd_region_notify(struct device *dev, enum nvdimm_event event)
12371999466SDan Williams {
1246a6bef90SDave Jiang 	if (event == NVDIMM_REVALIDATE_POISON) {
1256a6bef90SDave Jiang 		struct nd_region *nd_region = to_nd_region(dev);
1266a6bef90SDave Jiang 
127c42adf87SAneesh Kumar K.V 		if (is_memory(&nd_region->dev)) {
128a4574f63SDan Williams 			struct range range = {
129a4574f63SDan Williams 				.start = nd_region->ndr_start,
130a4574f63SDan Williams 				.end = nd_region->ndr_start +
131a4574f63SDan Williams 					nd_region->ndr_size - 1,
132a4574f63SDan Williams 			};
133a4574f63SDan Williams 
1346a6bef90SDave Jiang 			nvdimm_badblocks_populate(nd_region,
135a4574f63SDan Williams 					&nd_region->bb, &range);
136975750a9SToshi Kani 			if (nd_region->bb_state)
137975750a9SToshi Kani 				sysfs_notify_dirent(nd_region->bb_state);
1386a6bef90SDave Jiang 		}
1396a6bef90SDave Jiang 	}
14071999466SDan Williams 	device_for_each_child(dev, &event, child_notify);
14171999466SDan Williams }
14271999466SDan Williams 
1433d88002eSDan Williams static struct nd_device_driver nd_region_driver = {
1443d88002eSDan Williams 	.probe = nd_region_probe,
1453d88002eSDan Williams 	.remove = nd_region_remove,
14671999466SDan Williams 	.notify = nd_region_notify,
1473d88002eSDan Williams 	.drv = {
1483d88002eSDan Williams 		.name = "nd_region",
1493d88002eSDan Williams 	},
1503d88002eSDan Williams 	.type = ND_DRIVER_REGION_BLK | ND_DRIVER_REGION_PMEM,
1513d88002eSDan Williams };
1523d88002eSDan Williams 
nd_region_init(void)1533d88002eSDan Williams int __init nd_region_init(void)
1543d88002eSDan Williams {
1553d88002eSDan Williams 	return nd_driver_register(&nd_region_driver);
1563d88002eSDan Williams }
1573d88002eSDan Williams 
nd_region_exit(void)1583d88002eSDan Williams void nd_region_exit(void)
1593d88002eSDan Williams {
1603d88002eSDan Williams 	driver_unregister(&nd_region_driver.drv);
1613d88002eSDan Williams }
1623d88002eSDan Williams 
1633d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_PMEM);
164