Commit 04ad63f0 authored by Dan Williams's avatar Dan Williams
Browse files

cxl/region: Introduce cxl_pmem_region objects



The LIBNVDIMM subsystem is a platform agnostic representation of system
NVDIMM / persistent memory resources. To date, the CXL subsystem's
interaction with LIBNVDIMM has been to register an nvdimm-bridge device
and cxl_nvdimm objects to proxy CXL capabilities into existing LIBNVDIMM
subsystem mechanics.

With regions the approach is the same. Create a new cxl_pmem_region
object to proxy CXL region details into a LIBNVDIMM definition. With
this enabling LIBNVDIMM can partition CXL persistent memory regions with
legacy namespace labels. A follow-on patch will add CXL region label and
CXL namespace label support to persist region configurations across
driver reload / system-reset events.

Co-developed-by: default avatarBen Widawsky <bwidawsk@kernel.org>
Signed-off-by: default avatarBen Widawsky <bwidawsk@kernel.org>
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/165784340111.1758207.3036498385188290968.stgit@dwillia2-xfh.jf.intel.com


Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 99183d26
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -13,11 +13,13 @@ extern struct attribute_group cxl_base_attribute_group;
extern struct device_attribute dev_attr_create_pmem_region;
extern struct device_attribute dev_attr_delete_region;
extern struct device_attribute dev_attr_region;
extern const struct device_type cxl_pmem_region_type;
extern const struct device_type cxl_region_type;
void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
#define CXL_REGION_TYPE(x) (&cxl_region_type)
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
int cxl_region_init(void);
void cxl_region_exit(void);
#else
@@ -34,6 +36,7 @@ static inline void cxl_region_exit(void)
#define CXL_REGION_ATTR(x) NULL
#define CXL_REGION_TYPE(x) NULL
#define SET_CXL_REGION_ATTR(x)
#define CXL_PMEM_REGION_TYPE(x) NULL
#endif

struct cxl_send_command;
+2 −2
Original line number Diff line number Diff line
@@ -62,9 +62,9 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
	return is_cxl_nvdimm_bridge(dev);
}

struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd)
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct device *start)
{
	struct cxl_port *port = find_cxl_root(&cxl_nvd->dev);
	struct cxl_port *port = find_cxl_root(start);
	struct device *dev;

	if (!port)
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ static int cxl_device_id(struct device *dev)
		return CXL_DEVICE_NVDIMM_BRIDGE;
	if (dev->type == &cxl_nvdimm_type)
		return CXL_DEVICE_NVDIMM;
	if (dev->type == CXL_PMEM_REGION_TYPE())
		return CXL_DEVICE_PMEM_REGION;
	if (is_cxl_port(dev)) {
		if (is_cxl_root(to_cxl_port(dev)))
			return CXL_DEVICE_ROOT;
+141 −1
Original line number Diff line number Diff line
@@ -1650,6 +1650,139 @@ static ssize_t delete_region_store(struct device *dev,
}
DEVICE_ATTR_WO(delete_region);

static void cxl_pmem_region_release(struct device *dev)
{
	struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
	int i;

	for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
		struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;

		put_device(&cxlmd->dev);
	}

	kfree(cxlr_pmem);
}

static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
	&cxl_base_attribute_group,
	NULL,
};

const struct device_type cxl_pmem_region_type = {
	.name = "cxl_pmem_region",
	.release = cxl_pmem_region_release,
	.groups = cxl_pmem_region_attribute_groups,
};

bool is_cxl_pmem_region(struct device *dev)
{
	return dev->type == &cxl_pmem_region_type;
}
EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, CXL);

struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
{
	if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
			  "not a cxl_pmem_region device\n"))
		return NULL;
	return container_of(dev, struct cxl_pmem_region, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, CXL);

static struct lock_class_key cxl_pmem_region_key;

static struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr)
{
	struct cxl_region_params *p = &cxlr->params;
	struct cxl_pmem_region *cxlr_pmem;
	struct device *dev;
	int i;

	down_read(&cxl_region_rwsem);
	if (p->state != CXL_CONFIG_COMMIT) {
		cxlr_pmem = ERR_PTR(-ENXIO);
		goto out;
	}

	cxlr_pmem = kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets),
			    GFP_KERNEL);
	if (!cxlr_pmem) {
		cxlr_pmem = ERR_PTR(-ENOMEM);
		goto out;
	}

	cxlr_pmem->hpa_range.start = p->res->start;
	cxlr_pmem->hpa_range.end = p->res->end;

	/* Snapshot the region configuration underneath the cxl_region_rwsem */
	cxlr_pmem->nr_mappings = p->nr_targets;
	for (i = 0; i < p->nr_targets; i++) {
		struct cxl_endpoint_decoder *cxled = p->targets[i];
		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
		struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];

		m->cxlmd = cxlmd;
		get_device(&cxlmd->dev);
		m->start = cxled->dpa_res->start;
		m->size = resource_size(cxled->dpa_res);
		m->position = i;
	}

	dev = &cxlr_pmem->dev;
	cxlr_pmem->cxlr = cxlr;
	device_initialize(dev);
	lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
	device_set_pm_not_required(dev);
	dev->parent = &cxlr->dev;
	dev->bus = &cxl_bus_type;
	dev->type = &cxl_pmem_region_type;
out:
	up_read(&cxl_region_rwsem);

	return cxlr_pmem;
}

static void cxlr_pmem_unregister(void *dev)
{
	device_unregister(dev);
}

/**
 * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
 * @cxlr: parent CXL region for this pmem region bridge device
 *
 * Return: 0 on success negative error code on failure.
 */
static int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
{
	struct cxl_pmem_region *cxlr_pmem;
	struct device *dev;
	int rc;

	cxlr_pmem = cxl_pmem_region_alloc(cxlr);
	if (IS_ERR(cxlr_pmem))
		return PTR_ERR(cxlr_pmem);

	dev = &cxlr_pmem->dev;
	rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
	if (rc)
		goto err;

	rc = device_add(dev);
	if (rc)
		goto err;

	dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
		dev_name(dev));

	return devm_add_action_or_reset(&cxlr->dev, cxlr_pmem_unregister, dev);

err:
	put_device(dev);
	return rc;
}

static int cxl_region_probe(struct device *dev)
{
	struct cxl_region *cxlr = to_cxl_region(dev);
@@ -1673,7 +1806,14 @@ static int cxl_region_probe(struct device *dev)
	 */
	up_read(&cxl_region_rwsem);

	return rc;
	switch (cxlr->mode) {
	case CXL_DECODER_PMEM:
		return devm_cxl_add_pmem_region(cxlr);
	default:
		dev_dbg(&cxlr->dev, "unsupported region mode: %d\n",
			cxlr->mode);
		return -ENXIO;
	}
}

static struct cxl_driver cxl_region_driver = {
+35 −1
Original line number Diff line number Diff line
@@ -419,6 +419,25 @@ struct cxl_nvdimm {
	struct device dev;
	struct cxl_memdev *cxlmd;
	struct cxl_nvdimm_bridge *bridge;
	struct cxl_pmem_region *region;
};

struct cxl_pmem_region_mapping {
	struct cxl_memdev *cxlmd;
	struct cxl_nvdimm *cxl_nvd;
	u64 start;
	u64 size;
	int position;
};

struct cxl_pmem_region {
	struct device dev;
	struct cxl_region *cxlr;
	struct nd_region *nd_region;
	struct cxl_nvdimm_bridge *bridge;
	struct range hpa_range;
	int nr_mappings;
	struct cxl_pmem_region_mapping mapping[];
};

/**
@@ -601,6 +620,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
#define CXL_DEVICE_ROOT			4
#define CXL_DEVICE_MEMORY_EXPANDER	5
#define CXL_DEVICE_REGION		6
#define CXL_DEVICE_PMEM_REGION		7

#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
#define CXL_MODALIAS_FMT "cxl:t%d"
@@ -612,7 +632,21 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
bool is_cxl_nvdimm(struct device *dev);
bool is_cxl_nvdimm_bridge(struct device *dev);
int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd);
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct device *dev);

#ifdef CONFIG_CXL_REGION
bool is_cxl_pmem_region(struct device *dev);
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
	return false;
}
static inline struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
{
	return NULL;
}
#endif

/*
 * Unit test builds overrides this to __weak, find the 'strong' version
Loading