A private_region is just a RAM region which attempts to set the target_node to N_PRIVATE before continuing to create a DAX device and subsequently hotplugging memory onto the system. A CXL device driver would create a private_region with the intent to manage how the memory can be used more granuarly than typical SystemRAM. This patch adds the infrastructure for a private memory region. Added as a separate folder to keep private region types organized. usage: echo regionN > decoderX.Y/create_private_region echo type > regionN/private_type Signed-off-by: Gregory Price --- drivers/cxl/core/Makefile | 1 + drivers/cxl/core/core.h | 4 + drivers/cxl/core/port.c | 4 + drivers/cxl/core/private_region/Makefile | 9 ++ .../cxl/core/private_region/private_region.c | 119 ++++++++++++++++++ .../cxl/core/private_region/private_region.h | 10 ++ drivers/cxl/core/region.c | 63 ++++++++-- drivers/cxl/cxl.h | 20 +++ 8 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 drivers/cxl/core/private_region/Makefile create mode 100644 drivers/cxl/core/private_region/private_region.c create mode 100644 drivers/cxl/core/private_region/private_region.h diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 5ad8fef210b5..2dd882a52609 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -17,6 +17,7 @@ cxl_core-y += cdat.o cxl_core-y += ras.o cxl_core-$(CONFIG_TRACING) += trace.o cxl_core-$(CONFIG_CXL_REGION) += region.o +obj-$(CONFIG_CXL_REGION) += private_region/ cxl_core-$(CONFIG_CXL_MCE) += mce.o cxl_core-$(CONFIG_CXL_FEATURES) += features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 1fb66132b777..159f92e4bea1 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -21,6 +21,7 @@ enum cxl_detach_mode { #ifdef CONFIG_CXL_REGION extern struct device_attribute dev_attr_create_pmem_region; extern struct device_attribute dev_attr_create_ram_region; +extern struct device_attribute dev_attr_create_private_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; @@ -30,6 +31,9 @@ extern const struct device_type cxl_region_type; int cxl_decoder_detach(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, int pos, enum cxl_detach_mode mode); +int devm_cxl_add_dax_region(struct cxl_region *cxlr); +struct cxl_region *to_cxl_region(struct device *dev); +extern struct device_attribute dev_attr_private_type; #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr) #define CXL_REGION_TYPE(x) (&cxl_region_type) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index fef3aa0c6680..aedecb83e59b 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -333,6 +333,7 @@ static struct attribute *cxl_decoder_root_attrs[] = { &dev_attr_qos_class.attr, SET_CXL_REGION_ATTR(create_pmem_region) SET_CXL_REGION_ATTR(create_ram_region) + SET_CXL_REGION_ATTR(create_private_region) SET_CXL_REGION_ATTR(delete_region) NULL, }; @@ -362,6 +363,9 @@ static umode_t cxl_root_decoder_visible(struct kobject *kobj, struct attribute * if (a == CXL_REGION_ATTR(create_ram_region) && !can_create_ram(cxlrd)) return 0; + if (a == CXL_REGION_ATTR(create_private_region) && !can_create_ram(cxlrd)) + return 0; + if (a == CXL_REGION_ATTR(delete_region) && !(can_create_pmem(cxlrd) || can_create_ram(cxlrd))) return 0; diff --git a/drivers/cxl/core/private_region/Makefile b/drivers/cxl/core/private_region/Makefile new file mode 100644 index 000000000000..d17498129ba6 --- /dev/null +++ b/drivers/cxl/core/private_region/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CXL Private Region type implementations +# + +ccflags-y += -I$(srctree)/drivers/cxl + +# Core dispatch and sysfs +obj-$(CONFIG_CXL_REGION) += private_region.o diff --git a/drivers/cxl/core/private_region/private_region.c b/drivers/cxl/core/private_region/private_region.c new file mode 100644 index 000000000000..ead48abb9fc7 --- /dev/null +++ b/drivers/cxl/core/private_region/private_region.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CXL Private Region - dispatch and lifecycle management + * + * This file implements the main registration and unregistration dispatch + * for CXL private regions. It handles common initialization and delegates + * to type-specific implementations. + */ + +#include +#include +#include "../../cxl.h" +#include "../core.h" +#include "private_region.h" + +static const char *private_type_to_string(enum cxl_private_region_type type) +{ + switch (type) { + default: + return ""; + } +} + +static enum cxl_private_region_type string_to_private_type(const char *str) +{ + return CXL_PRIVATE_NONE; +} + +static ssize_t private_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + + return sysfs_emit(buf, "%s\n", private_type_to_string(cxlr->private_type)); +} + +static ssize_t private_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region_params *p = &cxlr->params; + enum cxl_private_region_type type; + ssize_t rc; + + type = string_to_private_type(buf); + if (type == CXL_PRIVATE_NONE) + return -EINVAL; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + /* Can only change type before region is committed */ + if (p->state >= CXL_CONFIG_COMMIT) + return -EBUSY; + + cxlr->private_type = type; + + return len; +} +DEVICE_ATTR_RW(private_type); + +/* + * Register a private CXL region based on its private_type. + * + * This function is called during commit. It validates the private_type, + * initializes the private_ops, and dispatches to the appropriate + * registration function which handles memtype, callbacks, and node + * registration. + */ +int cxl_register_private_region(struct cxl_region *cxlr) +{ + int rc = 0; + + if (!cxlr->params.res) + return -EINVAL; + + if (cxlr->private_type == CXL_PRIVATE_NONE) { + dev_err(&cxlr->dev, "private_type must be set before commit\n"); + return -EINVAL; + } + + /* Initialize the private_ops with region info */ + cxlr->private_ops.res_start = cxlr->params.res->start; + cxlr->private_ops.res_end = cxlr->params.res->end; + cxlr->private_ops.data = cxlr; + + /* Call type-specific registration which sets memtype and callbacks */ + switch (cxlr->private_type) { + default: + dev_dbg(&cxlr->dev, "unsupported private_type: %d\n", + cxlr->private_type); + rc = -EINVAL; + break; + } + + if (!rc) + set_bit(CXL_REGION_F_PRIVATE_REGISTERED, &cxlr->flags); + return rc; +} + +/* + * Unregister a private CXL region. + * + * This function is called during region reset or device release. + * It dispatches to the appropriate type-specific cleanup function. + */ +void cxl_unregister_private_region(struct cxl_region *cxlr) +{ + if (!test_and_clear_bit(CXL_REGION_F_PRIVATE_REGISTERED, &cxlr->flags)) + return; + + /* Dispatch to type-specific cleanup */ + switch (cxlr->private_type) { + default: + break; + } +} diff --git a/drivers/cxl/core/private_region/private_region.h b/drivers/cxl/core/private_region/private_region.h new file mode 100644 index 000000000000..9b34e51d8df4 --- /dev/null +++ b/drivers/cxl/core/private_region/private_region.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __CXL_PRIVATE_REGION_H__ +#define __CXL_PRIVATE_REGION_H__ + +struct cxl_region; + +int cxl_register_private_region(struct cxl_region *cxlr); +void cxl_unregister_private_region(struct cxl_region *cxlr); + +#endif /* __CXL_PRIVATE_REGION_H__ */ diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index ae899f68551f..c60eef96c0ca 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -15,6 +15,7 @@ #include #include #include "core.h" +#include "private_region/private_region.h" /** * DOC: cxl core region @@ -38,8 +39,6 @@ */ static nodemask_t nodemask_region_seen = NODE_MASK_NONE; -static struct cxl_region *to_cxl_region(struct device *dev); - #define __ACCESS_ATTR_RO(_level, _name) { \ .attr = { .name = __stringify(_name), .mode = 0444 }, \ .show = _name##_access##_level##_show, \ @@ -398,9 +397,6 @@ static int __commit(struct cxl_region *cxlr) return rc; rc = cxl_region_decode_commit(cxlr); - if (rc) - return rc; - p->state = CXL_CONFIG_COMMIT; return 0; @@ -615,12 +611,17 @@ static ssize_t mode_show(struct device *dev, struct device_attribute *attr, struct cxl_region *cxlr = to_cxl_region(dev); const char *desc; - if (cxlr->mode == CXL_PARTMODE_RAM) - desc = "ram"; - else if (cxlr->mode == CXL_PARTMODE_PMEM) + switch (cxlr->mode) { + case CXL_PARTMODE_RAM: + desc = cxlr->private ? "private" : "ram"; + break; + case CXL_PARTMODE_PMEM: desc = "pmem"; - else + break; + default: desc = ""; + break; + } return sysfs_emit(buf, "%s\n", desc); } @@ -772,6 +773,7 @@ static struct attribute *cxl_region_attrs[] = { &dev_attr_size.attr, &dev_attr_mode.attr, &dev_attr_extended_linear_cache_size.attr, + &dev_attr_private_type.attr, NULL, }; @@ -2400,6 +2402,9 @@ static void cxl_region_release(struct device *dev) struct cxl_region *cxlr = to_cxl_region(dev); int id = atomic_read(&cxlrd->region_id); + /* Ensure private region is cleaned up if not already done */ + cxl_unregister_private_region(cxlr); + /* * Try to reuse the recently idled id rather than the cached * next id to prevent the region id space from increasing @@ -2429,7 +2434,7 @@ bool is_cxl_region(struct device *dev) } EXPORT_SYMBOL_NS_GPL(is_cxl_region, "CXL"); -static struct cxl_region *to_cxl_region(struct device *dev) +struct cxl_region *to_cxl_region(struct device *dev) { if (dev_WARN_ONCE(dev, dev->type != &cxl_region_type, "not a cxl_region device\n")) @@ -2638,6 +2643,13 @@ static ssize_t create_ram_region_show(struct device *dev, return __create_region_show(to_cxl_root_decoder(dev), buf); } +static ssize_t create_private_region_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return __create_region_show(to_cxl_root_decoder(dev), buf); +} + static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, enum cxl_partition_mode mode, int id) { @@ -2698,6 +2710,28 @@ static ssize_t create_ram_region_store(struct device *dev, } DEVICE_ATTR_RW(create_ram_region); +static ssize_t create_private_region_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); + struct cxl_region *cxlr; + int rc, id; + + rc = sscanf(buf, "region%d\n", &id); + if (rc != 1) + return -EINVAL; + + cxlr = __create_region(cxlrd, CXL_PARTMODE_RAM, id); + if (IS_ERR(cxlr)) + return PTR_ERR(cxlr); + + cxlr->private = true; + + return len; +} +DEVICE_ATTR_RW(create_private_region); + static ssize_t region_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3431,7 +3465,7 @@ static void cxlr_dax_unregister(void *_cxlr_dax) device_unregister(&cxlr_dax->dev); } -static int devm_cxl_add_dax_region(struct cxl_region *cxlr) +int devm_cxl_add_dax_region(struct cxl_region *cxlr) { struct cxl_dax_region *cxlr_dax; struct device *dev; @@ -3974,6 +4008,13 @@ static int cxl_region_probe(struct device *dev) p->res->start, p->res->end, cxlr, is_system_ram) > 0) return 0; + + + if (cxlr->private) { + rc = cxl_register_private_region(cxlr); + if (rc) + return rc; + } return devm_cxl_add_dax_region(cxlr); default: dev_dbg(&cxlr->dev, "unsupported region mode: %d\n", diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index ba17fa86d249..b276956ff88d 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -525,6 +525,20 @@ enum cxl_partition_mode { */ #define CXL_REGION_F_LOCK 2 +/* + * Indicate that this region has been registered as a private region. + * Used to track lifecycle and prevent double-unregistration. + */ +#define CXL_REGION_F_PRIVATE_REGISTERED 3 + +/** + * enum cxl_private_region_type - CXL private region types + * @CXL_PRIVATE_NONE: No private region type set + */ +enum cxl_private_region_type { + CXL_PRIVATE_NONE, +}; + /** * struct cxl_region - CXL region * @dev: This region's device @@ -534,10 +548,13 @@ enum cxl_partition_mode { * @cxl_nvb: nvdimm bridge for coordinating @cxlr_pmem setup / shutdown * @cxlr_pmem: (for pmem regions) cached copy of the nvdimm bridge * @flags: Region state flags + * @private: Region is private (not exposed to system memory) * @params: active + config params for the region * @coord: QoS access coordinates for the region * @node_notifier: notifier for setting the access coordinates to node * @adist_notifier: notifier for calculating the abstract distance of node + * @private_type: CXL private region type for dispatch (set via sysfs) + * @private_ops: private node operations for callbacks (if mode is PRIVATE) */ struct cxl_region { struct device dev; @@ -547,10 +564,13 @@ struct cxl_region { struct cxl_nvdimm_bridge *cxl_nvb; struct cxl_pmem_region *cxlr_pmem; unsigned long flags; + bool private; struct cxl_region_params params; struct access_coordinate coord[ACCESS_COORDINATE_MAX]; struct notifier_block node_notifier; struct notifier_block adist_notifier; + enum cxl_private_region_type private_type; + struct private_node_ops private_ops; }; struct cxl_nvdimm_bridge { -- 2.52.0