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