--- v20
+++ v23
@@ -1,36 +1,205 @@
From: Alejandro Lucero <alucerop@amd.com>
-By definition a type2 cxl device will use the host managed memory for
-specific functionality, therefore it should not be available to other
-uses.
+Creating a CXL region requires userspace intervention through the cxl
+sysfs files. Type2 support should allow accelerator drivers to create
+such cxl region from kernel code.
+
+Adding that functionality and integrating it with current support for
+memory expanders.
+
+Based on https://lore.kernel.org/linux-cxl/168592159835.1948938.1647215579839222774.stgit@dwillia2-xfh.jf.intel.com/
Signed-off-by: Alejandro Lucero <alucerop@amd.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
-Reviewed-by: Davidlohr Bueso <daves@stgolabs.net>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
-Reviewed-by: Ben Cheatham <benjamin.cheatham@amd.com>
---
- drivers/cxl/core/region.c | 7 +++++++
- 1 file changed, 7 insertions(+)
+ drivers/cxl/core/region.c | 131 ++++++++++++++++++++++++++++++++++++--
+ include/cxl/cxl.h | 3 +
+ 2 files changed, 127 insertions(+), 7 deletions(-)
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
-index 63c9c5f92252..5ee40cb9d050 100644
+index 63c2aeb2ee1f..293e63dfef22 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
-@@ -4135,6 +4135,13 @@ static int cxl_region_probe(struct device *dev)
- return rc;
- }
-
+@@ -2944,6 +2944,14 @@ cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name)
+ return to_cxl_region(region_dev);
+ }
+
++static void drop_region(struct cxl_region *cxlr)
++{
++ struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
++ struct cxl_port *port = cxlrd_to_port(cxlrd);
++
++ devm_release_action(port->uport_dev, __unregister_region, cxlr);
++}
++
+ static ssize_t delete_region_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+@@ -4047,14 +4055,12 @@ static int __construct_region(struct cxl_region *cxlr,
+ return 0;
+ }
+
+-/* Establish an empty region covering the given HPA range */
+-static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
+- struct cxl_endpoint_decoder *cxled)
++static struct cxl_region *construct_region_begin(struct cxl_root_decoder *cxlrd,
++ struct cxl_endpoint_decoder *cxled)
+ {
+ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+- struct cxl_port *port = cxlrd_to_port(cxlrd);
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+- int rc, part = READ_ONCE(cxled->part);
++ int part = READ_ONCE(cxled->part);
+ struct cxl_region *cxlr;
+
+ do {
+@@ -4063,13 +4069,26 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
+ cxled->cxld.target_type);
+ } while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
+
+- if (IS_ERR(cxlr)) {
++ if (IS_ERR(cxlr))
+ dev_err(cxlmd->dev.parent,
+ "%s:%s: %s failed assign region: %ld\n",
+ dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
+ __func__, PTR_ERR(cxlr));
++
++ return cxlr;
++}
++
++/* Establish an empty region covering the given HPA range */
++static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
++ struct cxl_endpoint_decoder *cxled)
++{
++ struct cxl_port *port = cxlrd_to_port(cxlrd);
++ struct cxl_region *cxlr;
++ int rc;
++
++ cxlr = construct_region_begin(cxlrd, cxled);
++ if (IS_ERR(cxlr))
+ return cxlr;
+- }
+
+ rc = __construct_region(cxlr, cxlrd, cxled);
+ if (rc) {
+@@ -4080,6 +4099,104 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
+ return cxlr;
+ }
+
++DEFINE_FREE(cxl_region_drop, struct cxl_region *, if (_T) drop_region(_T))
++
++static struct cxl_region *
++__construct_new_region(struct cxl_root_decoder *cxlrd,
++ struct cxl_endpoint_decoder **cxled, int ways)
++{
++ struct cxl_memdev *cxlmd = cxled_to_memdev(cxled[0]);
++ struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
++ struct cxl_region_params *p;
++ resource_size_t size = 0;
++ int rc, i;
++
++ struct cxl_region *cxlr __free(cxl_region_drop) =
++ construct_region_begin(cxlrd, cxled[0]);
++ if (IS_ERR(cxlr))
++ return cxlr;
++
++ guard(rwsem_write)(&cxl_rwsem.region);
++
+ /*
-+ * HDM-D[B] (device-memory) regions have accelerator specific usage.
-+ * Skip device-dax registration.
++ * Sanity check. This should not happen with an accel driver handling
++ * the region creation.
+ */
-+ if (cxlr->type == CXL_DECODER_DEVMEM)
-+ return 0;
-+
- switch (cxlr->mode) {
- case CXL_PARTMODE_PMEM:
- rc = devm_cxl_region_edac_register(cxlr);
++ p = &cxlr->params;
++ if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) {
++ dev_err(cxlmd->dev.parent,
++ "%s:%s: %s unexpected region state\n",
++ dev_name(&cxlmd->dev), dev_name(&cxled[0]->cxld.dev),
++ __func__);
++ return ERR_PTR(-EBUSY);
++ }
++
++ rc = set_interleave_ways(cxlr, ways);
++ if (rc)
++ return ERR_PTR(rc);
++
++ rc = set_interleave_granularity(cxlr, cxld->interleave_granularity);
++ if (rc)
++ return ERR_PTR(rc);
++
++ scoped_guard(rwsem_read, &cxl_rwsem.dpa) {
++ for (i = 0; i < ways; i++) {
++ if (!cxled[i]->dpa_res)
++ return ERR_PTR(-EINVAL);
++ size += resource_size(cxled[i]->dpa_res);
++ }
++
++ rc = alloc_hpa(cxlr, size);
++ if (rc)
++ return ERR_PTR(rc);
++
++ for (i = 0; i < ways; i++) {
++ rc = cxl_region_attach(cxlr, cxled[i], 0);
++ if (rc)
++ return ERR_PTR(rc);
++ }
++ }
++
++ rc = cxl_region_decode_commit(cxlr);
++ if (rc)
++ return ERR_PTR(rc);
++
++ p->state = CXL_CONFIG_COMMIT;
++
++ return no_free_ptr(cxlr);
++}
++
++/**
++ * cxl_create_region - Establish a region given an endpoint decoder
++ * @cxlrd: root decoder to allocate HPA
++ * @cxled: endpoint decoders with reserved DPA capacity
++ * @ways: interleave ways required
++ *
++ * Returns a fully formed region in the commit state and attached to the
++ * cxl_region driver.
++ */
++struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
++ struct cxl_endpoint_decoder **cxled,
++ int ways)
++{
++ struct cxl_region *cxlr;
++
++ mutex_lock(&cxlrd->range_lock);
++ cxlr = __construct_new_region(cxlrd, cxled, ways);
++ mutex_unlock(&cxlrd->range_lock);
++ if (IS_ERR(cxlr))
++ return cxlr;
++
++ if (device_attach(&cxlr->dev) <= 0) {
++ dev_err(&cxlr->dev, "failed to create region\n");
++ drop_region(cxlr);
++ return ERR_PTR(-ENODEV);
++ }
++
++ return cxlr;
++}
++EXPORT_SYMBOL_NS_GPL(cxl_create_region, "CXL");
++
+ static struct cxl_region *
+ cxl_find_region_by_range(struct cxl_root_decoder *cxlrd, struct range *hpa)
+ {
+diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
+index 4802371db00e..50acbd13bcf8 100644
+--- a/include/cxl/cxl.h
++++ b/include/cxl/cxl.h
+@@ -281,4 +281,7 @@ struct cxl_endpoint_decoder *cxl_request_dpa(struct cxl_memdev *cxlmd,
+ enum cxl_partition_mode mode,
+ resource_size_t alloc);
+ int cxl_dpa_free(struct cxl_endpoint_decoder *cxled);
++struct cxl_region *cxl_create_region(struct cxl_root_decoder *cxlrd,
++ struct cxl_endpoint_decoder **cxled,
++ int ways);
+ #endif /* __CXL_CXL_H__ */
--
2.34.1