Skip to content

Commit 09d09e0

Browse files
committed
cxl/dax: Create dax devices for CXL RAM regions
While platform firmware takes some responsibility for mapping the RAM capacity of CXL devices present at boot, the OS is responsible for mapping the remainder and hot-added devices. Platform firmware is also responsible for identifying the platform general purpose memory pool, typically DDR attached DRAM, and arranging for the remainder to be 'Soft Reserved'. That reservation allows the CXL subsystem to route the memory to core-mm via memory-hotplug (dax_kmem), or leave it for dedicated access (device-dax). The new 'struct cxl_dax_region' object allows for a CXL memory resource (region) to be published, but also allow for udev and module policy to act on that event. It also prevents cxl_core.ko from having a module loading dependency on any drivers/dax/ modules. Tested-by: Fan Ni <[email protected]> Reviewed-by: Dave Jiang <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Link: https://lore.kernel.org/r/167602003896.1924368.10335442077318970468.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Dan Williams <[email protected]>
1 parent e9ee9fe commit 09d09e0

File tree

10 files changed

+209
-4
lines changed

10 files changed

+209
-4
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6035,6 +6035,7 @@ M: Dan Williams <[email protected]>
60356035
M: Vishal Verma <[email protected]>
60366036
M: Dave Jiang <[email protected]>
60376037
6038+
60386039
S: Supported
60396040
F: drivers/dax/
60406041

drivers/cxl/acpi.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,8 @@ static void __exit cxl_acpi_exit(void)
731731
cxl_bus_drain();
732732
}
733733

734-
module_init(cxl_acpi_init);
734+
/* load before dax_hmem sees 'Soft Reserved' CXL ranges */
735+
subsys_initcall(cxl_acpi_init);
735736
module_exit(cxl_acpi_exit);
736737
MODULE_LICENSE("GPL v2");
737738
MODULE_IMPORT_NS(CXL);

drivers/cxl/core/core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ extern struct device_attribute dev_attr_create_ram_region;
1515
extern struct device_attribute dev_attr_delete_region;
1616
extern struct device_attribute dev_attr_region;
1717
extern const struct device_type cxl_pmem_region_type;
18+
extern const struct device_type cxl_dax_region_type;
1819
extern const struct device_type cxl_region_type;
1920
void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
2021
#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
2122
#define CXL_REGION_TYPE(x) (&cxl_region_type)
2223
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
2324
#define CXL_PMEM_REGION_TYPE(x) (&cxl_pmem_region_type)
25+
#define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
2426
int cxl_region_init(void);
2527
void cxl_region_exit(void);
2628
#else
@@ -38,6 +40,7 @@ static inline void cxl_region_exit(void)
3840
#define CXL_REGION_TYPE(x) NULL
3941
#define SET_CXL_REGION_ATTR(x)
4042
#define CXL_PMEM_REGION_TYPE(x) NULL
43+
#define CXL_DAX_REGION_TYPE(x) NULL
4144
#endif
4245

4346
struct cxl_send_command;

drivers/cxl/core/port.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ static int cxl_device_id(struct device *dev)
4646
return CXL_DEVICE_NVDIMM;
4747
if (dev->type == CXL_PMEM_REGION_TYPE())
4848
return CXL_DEVICE_PMEM_REGION;
49+
if (dev->type == CXL_DAX_REGION_TYPE())
50+
return CXL_DEVICE_DAX_REGION;
4951
if (is_cxl_port(dev)) {
5052
if (is_cxl_root(to_cxl_port(dev)))
5153
return CXL_DEVICE_ROOT;
@@ -2015,6 +2017,6 @@ static void cxl_core_exit(void)
20152017
debugfs_remove_recursive(cxl_debugfs);
20162018
}
20172019

2018-
module_init(cxl_core_init);
2020+
subsys_initcall(cxl_core_init);
20192021
module_exit(cxl_core_exit);
20202022
MODULE_LICENSE("GPL v2");

drivers/cxl/core/region.c

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,75 @@ static struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr)
22782278
return cxlr_pmem;
22792279
}
22802280

2281+
static void cxl_dax_region_release(struct device *dev)
2282+
{
2283+
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
2284+
2285+
kfree(cxlr_dax);
2286+
}
2287+
2288+
static const struct attribute_group *cxl_dax_region_attribute_groups[] = {
2289+
&cxl_base_attribute_group,
2290+
NULL,
2291+
};
2292+
2293+
const struct device_type cxl_dax_region_type = {
2294+
.name = "cxl_dax_region",
2295+
.release = cxl_dax_region_release,
2296+
.groups = cxl_dax_region_attribute_groups,
2297+
};
2298+
2299+
static bool is_cxl_dax_region(struct device *dev)
2300+
{
2301+
return dev->type == &cxl_dax_region_type;
2302+
}
2303+
2304+
struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
2305+
{
2306+
if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
2307+
"not a cxl_dax_region device\n"))
2308+
return NULL;
2309+
return container_of(dev, struct cxl_dax_region, dev);
2310+
}
2311+
EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, CXL);
2312+
2313+
static struct lock_class_key cxl_dax_region_key;
2314+
2315+
static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
2316+
{
2317+
struct cxl_region_params *p = &cxlr->params;
2318+
struct cxl_dax_region *cxlr_dax;
2319+
struct device *dev;
2320+
2321+
down_read(&cxl_region_rwsem);
2322+
if (p->state != CXL_CONFIG_COMMIT) {
2323+
cxlr_dax = ERR_PTR(-ENXIO);
2324+
goto out;
2325+
}
2326+
2327+
cxlr_dax = kzalloc(sizeof(*cxlr_dax), GFP_KERNEL);
2328+
if (!cxlr_dax) {
2329+
cxlr_dax = ERR_PTR(-ENOMEM);
2330+
goto out;
2331+
}
2332+
2333+
cxlr_dax->hpa_range.start = p->res->start;
2334+
cxlr_dax->hpa_range.end = p->res->end;
2335+
2336+
dev = &cxlr_dax->dev;
2337+
cxlr_dax->cxlr = cxlr;
2338+
device_initialize(dev);
2339+
lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
2340+
device_set_pm_not_required(dev);
2341+
dev->parent = &cxlr->dev;
2342+
dev->bus = &cxl_bus_type;
2343+
dev->type = &cxl_dax_region_type;
2344+
out:
2345+
up_read(&cxl_region_rwsem);
2346+
2347+
return cxlr_dax;
2348+
}
2349+
22812350
static void cxlr_pmem_unregister(void *_cxlr_pmem)
22822351
{
22832352
struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
@@ -2362,6 +2431,42 @@ static int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
23622431
return rc;
23632432
}
23642433

2434+
static void cxlr_dax_unregister(void *_cxlr_dax)
2435+
{
2436+
struct cxl_dax_region *cxlr_dax = _cxlr_dax;
2437+
2438+
device_unregister(&cxlr_dax->dev);
2439+
}
2440+
2441+
static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
2442+
{
2443+
struct cxl_dax_region *cxlr_dax;
2444+
struct device *dev;
2445+
int rc;
2446+
2447+
cxlr_dax = cxl_dax_region_alloc(cxlr);
2448+
if (IS_ERR(cxlr_dax))
2449+
return PTR_ERR(cxlr_dax);
2450+
2451+
dev = &cxlr_dax->dev;
2452+
rc = dev_set_name(dev, "dax_region%d", cxlr->id);
2453+
if (rc)
2454+
goto err;
2455+
2456+
rc = device_add(dev);
2457+
if (rc)
2458+
goto err;
2459+
2460+
dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
2461+
dev_name(dev));
2462+
2463+
return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
2464+
cxlr_dax);
2465+
err:
2466+
put_device(dev);
2467+
return rc;
2468+
}
2469+
23652470
static int match_decoder_by_range(struct device *dev, void *data)
23662471
{
23672472
struct range *r1, *r2 = data;
@@ -2624,8 +2729,7 @@ static int cxl_region_probe(struct device *dev)
26242729
p->res->start, p->res->end, cxlr,
26252730
is_system_ram) > 0)
26262731
return 0;
2627-
dev_dbg(dev, "TODO: hookup devdax\n");
2628-
return 0;
2732+
return devm_cxl_add_dax_region(cxlr);
26292733
default:
26302734
dev_dbg(&cxlr->dev, "unsupported region mode: %d\n",
26312735
cxlr->mode);

drivers/cxl/cxl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,12 @@ struct cxl_pmem_region {
513513
struct cxl_pmem_region_mapping mapping[];
514514
};
515515

516+
struct cxl_dax_region {
517+
struct device dev;
518+
struct cxl_region *cxlr;
519+
struct range hpa_range;
520+
};
521+
516522
/**
517523
* struct cxl_port - logical collection of upstream port devices and
518524
* downstream port devices to construct a CXL memory
@@ -707,6 +713,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
707713
#define CXL_DEVICE_MEMORY_EXPANDER 5
708714
#define CXL_DEVICE_REGION 6
709715
#define CXL_DEVICE_PMEM_REGION 7
716+
#define CXL_DEVICE_DAX_REGION 8
710717

711718
#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
712719
#define CXL_MODALIAS_FMT "cxl:t%d"
@@ -725,6 +732,7 @@ bool is_cxl_pmem_region(struct device *dev);
725732
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
726733
int cxl_add_to_region(struct cxl_port *root,
727734
struct cxl_endpoint_decoder *cxled);
735+
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
728736
#else
729737
static inline bool is_cxl_pmem_region(struct device *dev)
730738
{
@@ -739,6 +747,10 @@ static inline int cxl_add_to_region(struct cxl_port *root,
739747
{
740748
return 0;
741749
}
750+
static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
751+
{
752+
return NULL;
753+
}
742754
#endif
743755

744756
/*

drivers/dax/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ config DEV_DAX_HMEM
4545

4646
Say M if unsure.
4747

48+
config DEV_DAX_CXL
49+
tristate "CXL DAX: direct access to CXL RAM regions"
50+
depends on CXL_REGION && DEV_DAX
51+
default CXL_REGION && DEV_DAX
52+
help
53+
CXL RAM regions are either mapped by platform-firmware
54+
and published in the initial system-memory map as "System RAM", mapped
55+
by platform-firmware as "Soft Reserved", or dynamically provisioned
56+
after boot by the CXL driver. In the latter two cases a device-dax
57+
instance is created to access that unmapped-by-default address range.
58+
Per usual it can remain as dedicated access via a device interface, or
59+
converted to "System RAM" via the dax_kmem facility.
60+
4861
config DEV_DAX_HMEM_DEVICES
4962
depends on DEV_DAX_HMEM && DAX
5063
def_bool y

drivers/dax/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ obj-$(CONFIG_DAX) += dax.o
33
obj-$(CONFIG_DEV_DAX) += device_dax.o
44
obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o
55
obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
6+
obj-$(CONFIG_DEV_DAX_CXL) += dax_cxl.o
67

78
dax-y := super.o
89
dax-y += bus.o
910
device_dax-y := device.o
1011
dax_pmem-y := pmem.o
12+
dax_cxl-y := cxl.o
1113

1214
obj-y += hmem/

drivers/dax/cxl.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright(c) 2023 Intel Corporation. All rights reserved. */
3+
#include <linux/module.h>
4+
#include <linux/dax.h>
5+
6+
#include "../cxl/cxl.h"
7+
#include "bus.h"
8+
9+
static int cxl_dax_region_probe(struct device *dev)
10+
{
11+
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
12+
int nid = phys_to_target_node(cxlr_dax->hpa_range.start);
13+
struct cxl_region *cxlr = cxlr_dax->cxlr;
14+
struct dax_region *dax_region;
15+
struct dev_dax_data data;
16+
struct dev_dax *dev_dax;
17+
18+
if (nid == NUMA_NO_NODE)
19+
nid = memory_add_physaddr_to_nid(cxlr_dax->hpa_range.start);
20+
21+
dax_region = alloc_dax_region(dev, cxlr->id, &cxlr_dax->hpa_range, nid,
22+
PMD_SIZE, IORESOURCE_DAX_KMEM);
23+
if (!dax_region)
24+
return -ENOMEM;
25+
26+
data = (struct dev_dax_data) {
27+
.dax_region = dax_region,
28+
.id = -1,
29+
.size = range_len(&cxlr_dax->hpa_range),
30+
};
31+
dev_dax = devm_create_dev_dax(&data);
32+
if (IS_ERR(dev_dax))
33+
return PTR_ERR(dev_dax);
34+
35+
/* child dev_dax instances now own the lifetime of the dax_region */
36+
dax_region_put(dax_region);
37+
return 0;
38+
}
39+
40+
static struct cxl_driver cxl_dax_region_driver = {
41+
.name = "cxl_dax_region",
42+
.probe = cxl_dax_region_probe,
43+
.id = CXL_DEVICE_DAX_REGION,
44+
.drv = {
45+
.suppress_bind_attrs = true,
46+
},
47+
};
48+
49+
module_cxl_driver(cxl_dax_region_driver);
50+
MODULE_ALIAS_CXL(CXL_DEVICE_DAX_REGION);
51+
MODULE_LICENSE("GPL");
52+
MODULE_AUTHOR("Intel Corporation");
53+
MODULE_IMPORT_NS(CXL);

drivers/dax/hmem/hmem.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ static int hmem_register_device(struct device *host, int target_nid,
7272
long id;
7373
int rc;
7474

75+
if (IS_ENABLED(CONFIG_CXL_REGION) &&
76+
region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
77+
IORES_DESC_CXL) != REGION_DISJOINT) {
78+
dev_dbg(host, "deferring range to CXL: %pr\n", res);
79+
return 0;
80+
}
81+
7582
rc = region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
7683
IORES_DESC_SOFT_RESERVED);
7784
if (rc != REGION_INTERSECTS)
@@ -157,6 +164,13 @@ static __exit void dax_hmem_exit(void)
157164
module_init(dax_hmem_init);
158165
module_exit(dax_hmem_exit);
159166

167+
/* Allow for CXL to define its own dax regions */
168+
#if IS_ENABLED(CONFIG_CXL_REGION)
169+
#if IS_MODULE(CONFIG_CXL_ACPI)
170+
MODULE_SOFTDEP("pre: cxl_acpi");
171+
#endif
172+
#endif
173+
160174
MODULE_ALIAS("platform:hmem*");
161175
MODULE_ALIAS("platform:hmem_platform*");
162176
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)