xref: /linux/drivers/cxl/core/region_pmem.c (revision 12bffaef28820e0b94c644c75708195c61af78f7)
1*8a1ec5fbSGregory Price // SPDX-License-Identifier: GPL-2.0-only
2*8a1ec5fbSGregory Price /* Copyright(c) 2022 Intel Corporation. All rights reserved. */
3*8a1ec5fbSGregory Price #include <linux/device.h>
4*8a1ec5fbSGregory Price #include <linux/slab.h>
5*8a1ec5fbSGregory Price #include <cxlmem.h>
6*8a1ec5fbSGregory Price #include <cxl.h>
7*8a1ec5fbSGregory Price #include "core.h"
8*8a1ec5fbSGregory Price 
9*8a1ec5fbSGregory Price static void cxl_pmem_region_release(struct device *dev)
10*8a1ec5fbSGregory Price {
11*8a1ec5fbSGregory Price 	struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
12*8a1ec5fbSGregory Price 	int i;
13*8a1ec5fbSGregory Price 
14*8a1ec5fbSGregory Price 	for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
15*8a1ec5fbSGregory Price 		struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
16*8a1ec5fbSGregory Price 
17*8a1ec5fbSGregory Price 		put_device(&cxlmd->dev);
18*8a1ec5fbSGregory Price 	}
19*8a1ec5fbSGregory Price 
20*8a1ec5fbSGregory Price 	kfree(cxlr_pmem);
21*8a1ec5fbSGregory Price }
22*8a1ec5fbSGregory Price 
23*8a1ec5fbSGregory Price static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
24*8a1ec5fbSGregory Price 	&cxl_base_attribute_group,
25*8a1ec5fbSGregory Price 	NULL
26*8a1ec5fbSGregory Price };
27*8a1ec5fbSGregory Price 
28*8a1ec5fbSGregory Price const struct device_type cxl_pmem_region_type = {
29*8a1ec5fbSGregory Price 	.name = "cxl_pmem_region",
30*8a1ec5fbSGregory Price 	.release = cxl_pmem_region_release,
31*8a1ec5fbSGregory Price 	.groups = cxl_pmem_region_attribute_groups,
32*8a1ec5fbSGregory Price };
33*8a1ec5fbSGregory Price 
34*8a1ec5fbSGregory Price bool is_cxl_pmem_region(struct device *dev)
35*8a1ec5fbSGregory Price {
36*8a1ec5fbSGregory Price 	return dev->type == &cxl_pmem_region_type;
37*8a1ec5fbSGregory Price }
38*8a1ec5fbSGregory Price EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
39*8a1ec5fbSGregory Price 
40*8a1ec5fbSGregory Price struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
41*8a1ec5fbSGregory Price {
42*8a1ec5fbSGregory Price 	if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
43*8a1ec5fbSGregory Price 			  "not a cxl_pmem_region device\n"))
44*8a1ec5fbSGregory Price 		return NULL;
45*8a1ec5fbSGregory Price 	return container_of(dev, struct cxl_pmem_region, dev);
46*8a1ec5fbSGregory Price }
47*8a1ec5fbSGregory Price EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
48*8a1ec5fbSGregory Price 
49*8a1ec5fbSGregory Price static struct lock_class_key cxl_pmem_region_key;
50*8a1ec5fbSGregory Price 
51*8a1ec5fbSGregory Price static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
52*8a1ec5fbSGregory Price {
53*8a1ec5fbSGregory Price 	struct cxl_region_params *p = &cxlr->params;
54*8a1ec5fbSGregory Price 	struct cxl_nvdimm_bridge *cxl_nvb;
55*8a1ec5fbSGregory Price 	struct device *dev;
56*8a1ec5fbSGregory Price 	int i;
57*8a1ec5fbSGregory Price 
58*8a1ec5fbSGregory Price 	guard(rwsem_read)(&cxl_rwsem.region);
59*8a1ec5fbSGregory Price 	if (p->state != CXL_CONFIG_COMMIT)
60*8a1ec5fbSGregory Price 		return -ENXIO;
61*8a1ec5fbSGregory Price 
62*8a1ec5fbSGregory Price 	struct cxl_pmem_region *cxlr_pmem __free(kfree) =
63*8a1ec5fbSGregory Price 		kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets);
64*8a1ec5fbSGregory Price 	if (!cxlr_pmem)
65*8a1ec5fbSGregory Price 		return -ENOMEM;
66*8a1ec5fbSGregory Price 
67*8a1ec5fbSGregory Price 	cxlr_pmem->hpa_range.start = p->res->start;
68*8a1ec5fbSGregory Price 	cxlr_pmem->hpa_range.end = p->res->end;
69*8a1ec5fbSGregory Price 
70*8a1ec5fbSGregory Price 	/* Snapshot the region configuration underneath the cxl_rwsem.region */
71*8a1ec5fbSGregory Price 	cxlr_pmem->nr_mappings = p->nr_targets;
72*8a1ec5fbSGregory Price 	for (i = 0; i < p->nr_targets; i++) {
73*8a1ec5fbSGregory Price 		struct cxl_endpoint_decoder *cxled = p->targets[i];
74*8a1ec5fbSGregory Price 		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
75*8a1ec5fbSGregory Price 		struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
76*8a1ec5fbSGregory Price 
77*8a1ec5fbSGregory Price 		/*
78*8a1ec5fbSGregory Price 		 * Regions never span CXL root devices, so by definition the
79*8a1ec5fbSGregory Price 		 * bridge for one device is the same for all.
80*8a1ec5fbSGregory Price 		 */
81*8a1ec5fbSGregory Price 		if (i == 0) {
82*8a1ec5fbSGregory Price 			cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
83*8a1ec5fbSGregory Price 			if (!cxl_nvb)
84*8a1ec5fbSGregory Price 				return -ENODEV;
85*8a1ec5fbSGregory Price 			cxlr->cxl_nvb = cxl_nvb;
86*8a1ec5fbSGregory Price 		}
87*8a1ec5fbSGregory Price 		m->cxlmd = cxlmd;
88*8a1ec5fbSGregory Price 		get_device(&cxlmd->dev);
89*8a1ec5fbSGregory Price 		m->start = cxled->dpa_res->start;
90*8a1ec5fbSGregory Price 		m->size = resource_size(cxled->dpa_res);
91*8a1ec5fbSGregory Price 		m->position = i;
92*8a1ec5fbSGregory Price 	}
93*8a1ec5fbSGregory Price 
94*8a1ec5fbSGregory Price 	dev = &cxlr_pmem->dev;
95*8a1ec5fbSGregory Price 	device_initialize(dev);
96*8a1ec5fbSGregory Price 	lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
97*8a1ec5fbSGregory Price 	device_set_pm_not_required(dev);
98*8a1ec5fbSGregory Price 	dev->parent = &cxlr->dev;
99*8a1ec5fbSGregory Price 	dev->bus = &cxl_bus_type;
100*8a1ec5fbSGregory Price 	dev->type = &cxl_pmem_region_type;
101*8a1ec5fbSGregory Price 	cxlr_pmem->cxlr = cxlr;
102*8a1ec5fbSGregory Price 	cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
103*8a1ec5fbSGregory Price 
104*8a1ec5fbSGregory Price 	return 0;
105*8a1ec5fbSGregory Price }
106*8a1ec5fbSGregory Price 
107*8a1ec5fbSGregory Price static void cxlr_pmem_unregister(void *_cxlr_pmem)
108*8a1ec5fbSGregory Price {
109*8a1ec5fbSGregory Price 	struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
110*8a1ec5fbSGregory Price 	struct cxl_region *cxlr = cxlr_pmem->cxlr;
111*8a1ec5fbSGregory Price 	struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
112*8a1ec5fbSGregory Price 
113*8a1ec5fbSGregory Price 	/*
114*8a1ec5fbSGregory Price 	 * Either the bridge is in ->remove() context under the device_lock(),
115*8a1ec5fbSGregory Price 	 * or cxlr_release_nvdimm() is cancelling the bridge's release action
116*8a1ec5fbSGregory Price 	 * for @cxlr_pmem and doing it itself (while manually holding the bridge
117*8a1ec5fbSGregory Price 	 * lock).
118*8a1ec5fbSGregory Price 	 */
119*8a1ec5fbSGregory Price 	device_lock_assert(&cxl_nvb->dev);
120*8a1ec5fbSGregory Price 	cxlr->cxlr_pmem = NULL;
121*8a1ec5fbSGregory Price 	cxlr_pmem->cxlr = NULL;
122*8a1ec5fbSGregory Price 	device_unregister(&cxlr_pmem->dev);
123*8a1ec5fbSGregory Price }
124*8a1ec5fbSGregory Price 
125*8a1ec5fbSGregory Price static void cxlr_release_nvdimm(void *_cxlr)
126*8a1ec5fbSGregory Price {
127*8a1ec5fbSGregory Price 	struct cxl_region *cxlr = _cxlr;
128*8a1ec5fbSGregory Price 	struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
129*8a1ec5fbSGregory Price 
130*8a1ec5fbSGregory Price 	scoped_guard(device, &cxl_nvb->dev) {
131*8a1ec5fbSGregory Price 		if (cxlr->cxlr_pmem)
132*8a1ec5fbSGregory Price 			devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
133*8a1ec5fbSGregory Price 					    cxlr->cxlr_pmem);
134*8a1ec5fbSGregory Price 	}
135*8a1ec5fbSGregory Price 	cxlr->cxl_nvb = NULL;
136*8a1ec5fbSGregory Price 	put_device(&cxl_nvb->dev);
137*8a1ec5fbSGregory Price }
138*8a1ec5fbSGregory Price 
139*8a1ec5fbSGregory Price /**
140*8a1ec5fbSGregory Price  * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
141*8a1ec5fbSGregory Price  * @cxlr: parent CXL region for this pmem region bridge device
142*8a1ec5fbSGregory Price  *
143*8a1ec5fbSGregory Price  * Return: 0 on success negative error code on failure.
144*8a1ec5fbSGregory Price  */
145*8a1ec5fbSGregory Price int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
146*8a1ec5fbSGregory Price {
147*8a1ec5fbSGregory Price 	struct cxl_pmem_region *cxlr_pmem;
148*8a1ec5fbSGregory Price 	struct cxl_nvdimm_bridge *cxl_nvb;
149*8a1ec5fbSGregory Price 	struct device *dev;
150*8a1ec5fbSGregory Price 	int rc;
151*8a1ec5fbSGregory Price 
152*8a1ec5fbSGregory Price 	rc = cxl_pmem_region_alloc(cxlr);
153*8a1ec5fbSGregory Price 	if (rc)
154*8a1ec5fbSGregory Price 		return rc;
155*8a1ec5fbSGregory Price 	cxlr_pmem = cxlr->cxlr_pmem;
156*8a1ec5fbSGregory Price 	cxl_nvb = cxlr->cxl_nvb;
157*8a1ec5fbSGregory Price 
158*8a1ec5fbSGregory Price 	dev = &cxlr_pmem->dev;
159*8a1ec5fbSGregory Price 	rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
160*8a1ec5fbSGregory Price 	if (rc)
161*8a1ec5fbSGregory Price 		goto err;
162*8a1ec5fbSGregory Price 
163*8a1ec5fbSGregory Price 	rc = device_add(dev);
164*8a1ec5fbSGregory Price 	if (rc)
165*8a1ec5fbSGregory Price 		goto err;
166*8a1ec5fbSGregory Price 
167*8a1ec5fbSGregory Price 	dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
168*8a1ec5fbSGregory Price 		dev_name(dev));
169*8a1ec5fbSGregory Price 
170*8a1ec5fbSGregory Price 	scoped_guard(device, &cxl_nvb->dev) {
171*8a1ec5fbSGregory Price 		if (cxl_nvb->dev.driver)
172*8a1ec5fbSGregory Price 			rc = devm_add_action_or_reset(&cxl_nvb->dev,
173*8a1ec5fbSGregory Price 						      cxlr_pmem_unregister,
174*8a1ec5fbSGregory Price 						      cxlr_pmem);
175*8a1ec5fbSGregory Price 		else
176*8a1ec5fbSGregory Price 			rc = -ENXIO;
177*8a1ec5fbSGregory Price 	}
178*8a1ec5fbSGregory Price 
179*8a1ec5fbSGregory Price 	if (rc)
180*8a1ec5fbSGregory Price 		goto err_bridge;
181*8a1ec5fbSGregory Price 
182*8a1ec5fbSGregory Price 	/* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
183*8a1ec5fbSGregory Price 	return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
184*8a1ec5fbSGregory Price 
185*8a1ec5fbSGregory Price err:
186*8a1ec5fbSGregory Price 	put_device(dev);
187*8a1ec5fbSGregory Price err_bridge:
188*8a1ec5fbSGregory Price 	put_device(&cxl_nvb->dev);
189*8a1ec5fbSGregory Price 	cxlr->cxl_nvb = NULL;
190*8a1ec5fbSGregory Price 	return rc;
191*8a1ec5fbSGregory Price }
192