@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/genalloc.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -637,6 +638,24 @@ devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets,
rc = devm_add_action_or_reset(host, unregister_cxl_dev, dev);
if (rc)
return ERR_PTR(rc);
+
+ if (dev->type == &cxl_decoder_root_type) {
+ struct gen_pool *pool;
+ int order = ilog2(SZ_256M * cxld->interleave_ways);
+
+ pool = devm_gen_pool_create(dev, order, NUMA_NO_NODE,
+ dev_name(dev));
+ if (IS_ERR(pool))
+ return ERR_CAST(pool);
+
+ cxld->address_space = pool;
+
+ rc = gen_pool_add(cxld->address_space, cxld->res.start,
+ resource_size(&cxld->res), NUMA_NO_NODE);
+ if (rc)
+ return ERR_PTR(rc);
+ }
+
return cxld;
err:
@@ -194,6 +194,7 @@ enum cxl_decoder_type {
* @region_ida: allocator for region ids.
* @regions: List of regions mapped (may be disabled) by this decoder.
* @youngest: Last region created for this decoder.
+ * @address_space: Used/free address space for regions.
* @target: active ordered target list in current decoder configuration
*/
struct cxl_decoder {
@@ -207,6 +208,7 @@ struct cxl_decoder {
struct ida region_ida;
struct list_head regions;
struct cxl_region *youngest;
+ struct gen_pool *address_space;
struct cxl_dport *target[];
};
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
+#include <linux/genalloc.h>
#include <linux/device.h>
#include <linux/module.h>
#include "region.h"
@@ -23,9 +24,34 @@
* relationship between decoder and region when the region is interleaved.
*/
+static int allocate_region_addr(struct cxl_region *region)
+{
+ struct cxl_decoder *cxld = to_cxl_decoder(region->dev.parent);
+ unsigned long start;
+
+ start = gen_pool_alloc(cxld->address_space, region->requested_size);
+ if (!start) {
+ trace_cxl_region_bind(region,
+ "Couldn't allocate address space");
+ return -ENOMEM;
+ }
+
+ region->res =
+ __request_region(&cxld->res, start, region->requested_size,
+ dev_name(®ion->dev), IORESOURCE_EXCLUSIVE);
+ if (IS_ERR(region->res)) {
+ trace_cxl_region_bind(region, "Couldn't obtain region");
+ gen_pool_free(cxld->address_space, start,
+ region->requested_size);
+ return PTR_ERR(region->res);
+ }
+
+ return 0;
+}
+
static int bind_region(struct cxl_region *region)
{
- int i;
+ int i, rc;
if (dev_WARN_ONCE(®ion->dev, !is_cxl_region_configured(region),
"unconfigured regions can't be probed (race?)\n")) {
@@ -43,7 +69,9 @@ static int bind_region(struct cxl_region *region)
return -ENXIO;
}
- /* TODO: Allocate from decoder's address space */
+ rc = allocate_region_addr(region);
+ if (rc)
+ return rc;
/* TODO: program HDM decoders */
Regions are carved out of an addresses space which is claimed by top level decoders, and subsequently their children decoders. Regions are created with a size and therefore must fit, with proper alignment, in that address space. The support for doing this fitting is handled by the driver automatically. As an example, a platform might configure a top level decoder to claim 1TB of address space @ 0x800000000 -> 0x10800000000; it would be possible to create M regions with appropriate alignment to occupy that address space. Each of those regions would have a host physical address somewhere in the range between 32G and 1.3TB, and the location will be determined by the logic added here. The request_region() usage is not strictly mandatory at this point as the actual handling of the address space is done with genpools. It is highly likely however that the resource/region APIs will become useful in the not too distant future. Signed-off-by: Ben Widawsky <ben.widawsky@intel.com> --- drivers/cxl/core/bus.c | 19 +++++++++++++++++++ drivers/cxl/cxl.h | 2 ++ drivers/cxl/region.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-)