Skip to content

Commit f0e6a23

Browse files
committed
cxl: Add Get Supported Features command for kernel usage
CXL spec r3.2 8.2.9.6.1 Get Supported Features (Opcode 0500h) The command retrieve the list of supported device-specific features (identified by UUID) and general information about each Feature. The driver will retrieve the Feature entries in order to make checks and provide information for the Get Feature and Set Feature command. One of the main piece of information retrieved are the effects a Set Feature command would have for a particular feature. The retrieved Feature entries are stored in the cxl_mailbox context. The setup of Features is initiated via devm_cxl_setup_features() during the pci probe function before the cxl_memdev is enumerated. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Dan Williams <dan.j.williams@intel.com> Reviewed-by: Li Ming <ming.li@zohomail.com> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Tested-by: Shiju Jose <shiju.jose@huawei.com> Link: https://patch.msgid.link/20250220194438.2281088-3-dave.jiang@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
1 parent cbbca60 commit f0e6a23

File tree

8 files changed

+287
-0
lines changed

8 files changed

+287
-0
lines changed

drivers/cxl/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ config CXL_MEM
102102

103103
If unsure say 'm'.
104104

105+
config CXL_FEATURES
106+
bool "CXL: Features"
107+
depends on CXL_PCI
108+
help
109+
Enable support for CXL Features. A CXL device that includes a mailbox
110+
supports commands that allows listing, getting, and setting of
111+
optionally defined features such as memory sparing or post package
112+
sparing. Vendors may define custom features for the device.
113+
114+
If unsure say 'n'
115+
105116
config CXL_PORT
106117
default CXL_BUS
107118
tristate

drivers/cxl/core/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ cxl_core-y += pmu.o
1616
cxl_core-y += cdat.o
1717
cxl_core-$(CONFIG_TRACING) += trace.o
1818
cxl_core-$(CONFIG_CXL_REGION) += region.o
19+
cxl_core-$(CONFIG_CXL_FEATURES) += features.o

drivers/cxl/core/features.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
3+
#include <linux/device.h>
4+
#include <cxl/mailbox.h>
5+
#include <cxl/features.h>
6+
#include "cxl.h"
7+
#include "cxlmem.h"
8+
9+
inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
10+
{
11+
return cxlds->cxlfs;
12+
}
13+
EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL");
14+
15+
static int cxl_get_supported_features_count(struct cxl_mailbox *cxl_mbox)
16+
{
17+
struct cxl_mbox_get_sup_feats_out mbox_out;
18+
struct cxl_mbox_get_sup_feats_in mbox_in;
19+
struct cxl_mbox_cmd mbox_cmd;
20+
int rc;
21+
22+
memset(&mbox_in, 0, sizeof(mbox_in));
23+
mbox_in.count = cpu_to_le32(sizeof(mbox_out));
24+
memset(&mbox_out, 0, sizeof(mbox_out));
25+
mbox_cmd = (struct cxl_mbox_cmd) {
26+
.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
27+
.size_in = sizeof(mbox_in),
28+
.payload_in = &mbox_in,
29+
.size_out = sizeof(mbox_out),
30+
.payload_out = &mbox_out,
31+
.min_out = sizeof(mbox_out),
32+
};
33+
rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
34+
if (rc < 0)
35+
return rc;
36+
37+
return le16_to_cpu(mbox_out.supported_feats);
38+
}
39+
40+
static struct cxl_feat_entries *
41+
get_supported_features(struct cxl_features_state *cxlfs)
42+
{
43+
int remain_feats, max_size, max_feats, start, rc, hdr_size;
44+
struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox;
45+
int feat_size = sizeof(struct cxl_feat_entry);
46+
struct cxl_mbox_get_sup_feats_in mbox_in;
47+
struct cxl_feat_entry *entry;
48+
struct cxl_mbox_cmd mbox_cmd;
49+
int count;
50+
51+
count = cxl_get_supported_features_count(cxl_mbox);
52+
if (count <= 0)
53+
return NULL;
54+
55+
struct cxl_feat_entries *entries __free(kvfree) =
56+
kvmalloc(struct_size(entries, ent, count), GFP_KERNEL);
57+
if (!entries)
58+
return NULL;
59+
60+
struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) =
61+
kvmalloc(cxl_mbox->payload_size, GFP_KERNEL);
62+
if (!mbox_out)
63+
return NULL;
64+
65+
hdr_size = struct_size(mbox_out, ents, 0);
66+
max_size = cxl_mbox->payload_size - hdr_size;
67+
/* max feat entries that can fit in mailbox max payload size */
68+
max_feats = max_size / feat_size;
69+
entry = entries->ent;
70+
71+
start = 0;
72+
remain_feats = count;
73+
do {
74+
int retrieved, alloc_size, copy_feats;
75+
int num_entries;
76+
77+
if (remain_feats > max_feats) {
78+
alloc_size = struct_size(mbox_out, ents, max_feats);
79+
remain_feats = remain_feats - max_feats;
80+
copy_feats = max_feats;
81+
} else {
82+
alloc_size = struct_size(mbox_out, ents, remain_feats);
83+
copy_feats = remain_feats;
84+
remain_feats = 0;
85+
}
86+
87+
memset(&mbox_in, 0, sizeof(mbox_in));
88+
mbox_in.count = cpu_to_le32(alloc_size);
89+
mbox_in.start_idx = cpu_to_le16(start);
90+
memset(mbox_out, 0, alloc_size);
91+
mbox_cmd = (struct cxl_mbox_cmd) {
92+
.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
93+
.size_in = sizeof(mbox_in),
94+
.payload_in = &mbox_in,
95+
.size_out = alloc_size,
96+
.payload_out = mbox_out,
97+
.min_out = hdr_size,
98+
};
99+
rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
100+
if (rc < 0)
101+
return NULL;
102+
103+
if (mbox_cmd.size_out <= hdr_size)
104+
return NULL;
105+
106+
/*
107+
* Make sure retrieved out buffer is multiple of feature
108+
* entries.
109+
*/
110+
retrieved = mbox_cmd.size_out - hdr_size;
111+
if (retrieved % feat_size)
112+
return NULL;
113+
114+
num_entries = le16_to_cpu(mbox_out->num_entries);
115+
/*
116+
* If the reported output entries * defined entry size !=
117+
* retrieved output bytes, then the output package is incorrect.
118+
*/
119+
if (num_entries * feat_size != retrieved)
120+
return NULL;
121+
122+
memcpy(entry, mbox_out->ents, retrieved);
123+
entry += num_entries;
124+
/*
125+
* If the number of output entries is less than expected, add the
126+
* remaining entries to the next batch.
127+
*/
128+
remain_feats += copy_feats - num_entries;
129+
start += num_entries;
130+
} while (remain_feats);
131+
132+
entries->num_features = count;
133+
134+
return no_free_ptr(entries);
135+
}
136+
137+
static void free_cxlfs(void *_cxlfs)
138+
{
139+
struct cxl_features_state *cxlfs = _cxlfs;
140+
struct cxl_dev_state *cxlds = cxlfs->cxlds;
141+
142+
cxlds->cxlfs = NULL;
143+
kvfree(cxlfs->entries);
144+
kfree(cxlfs);
145+
}
146+
147+
/**
148+
* devm_cxl_setup_features() - Allocate and initialize features context
149+
* @cxlds: CXL device context
150+
*
151+
* Return 0 on success or -errno on failure.
152+
*/
153+
int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
154+
{
155+
struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox;
156+
157+
if (cxl_mbox->feat_cap < CXL_FEATURES_RO)
158+
return -ENODEV;
159+
160+
struct cxl_features_state *cxlfs __free(kfree) =
161+
kzalloc(sizeof(*cxlfs), GFP_KERNEL);
162+
if (!cxlfs)
163+
return -ENOMEM;
164+
165+
cxlfs->cxlds = cxlds;
166+
167+
cxlfs->entries = get_supported_features(cxlfs);
168+
if (!cxlfs->entries)
169+
return -ENOMEM;
170+
171+
cxlds->cxlfs = cxlfs;
172+
173+
return devm_add_action_or_reset(cxlds->dev, free_cxlfs, no_free_ptr(cxlfs));
174+
}
175+
EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features, "CXL");

drivers/cxl/cxlmem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ struct cxl_dpa_perf {
392392
* @serial: PCIe Device Serial Number
393393
* @type: Generic Memory Class device or Vendor Specific Memory device
394394
* @cxl_mbox: CXL mailbox context
395+
* @cxlfs: CXL features context
395396
*/
396397
struct cxl_dev_state {
397398
struct device *dev;
@@ -407,6 +408,9 @@ struct cxl_dev_state {
407408
u64 serial;
408409
enum cxl_devtype type;
409410
struct cxl_mailbox cxl_mbox;
411+
#ifdef CONFIG_CXL_FEATURES
412+
struct cxl_features_state *cxlfs;
413+
#endif
410414
};
411415

412416
static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)

drivers/cxl/pci.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
997997
if (rc)
998998
return rc;
999999

1000+
rc = devm_cxl_setup_features(cxlds);
1001+
if (rc)
1002+
dev_dbg(&pdev->dev, "No CXL Features discovered\n");
1003+
10001004
cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
10011005
if (IS_ERR(cxlmd))
10021006
return PTR_ERR(cxlmd);

include/cxl/features.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,98 @@
33
#ifndef __CXL_FEATURES_H__
44
#define __CXL_FEATURES_H__
55

6+
#include <linux/uuid.h>
7+
68
/* Feature commands capability supported by a device */
79
enum cxl_features_capability {
810
CXL_FEATURES_NONE = 0,
911
CXL_FEATURES_RO,
1012
CXL_FEATURES_RW,
1113
};
1214

15+
/* Get Supported Features (0x500h) CXL r3.2 8.2.9.6.1 */
16+
struct cxl_mbox_get_sup_feats_in {
17+
__le32 count;
18+
__le16 start_idx;
19+
u8 reserved[2];
20+
} __packed;
21+
22+
/* CXL spec r3.2 Table 8-87 command effects */
23+
#define CXL_CMD_CONFIG_CHANGE_COLD_RESET BIT(0)
24+
#define CXL_CMD_CONFIG_CHANGE_IMMEDIATE BIT(1)
25+
#define CXL_CMD_DATA_CHANGE_IMMEDIATE BIT(2)
26+
#define CXL_CMD_POLICY_CHANGE_IMMEDIATE BIT(3)
27+
#define CXL_CMD_LOG_CHANGE_IMMEDIATE BIT(4)
28+
#define CXL_CMD_SECURITY_STATE_CHANGE BIT(5)
29+
#define CXL_CMD_BACKGROUND BIT(6)
30+
#define CXL_CMD_BGCMD_ABORT_SUPPORTED BIT(7)
31+
#define CXL_CMD_EFFECTS_VALID BIT(9)
32+
#define CXL_CMD_CONFIG_CHANGE_CONV_RESET BIT(10)
33+
#define CXL_CMD_CONFIG_CHANGE_CXL_RESET BIT(11)
34+
35+
/*
36+
* CXL spec r3.2 Table 8-109
37+
* Get Supported Features Supported Feature Entry
38+
*/
39+
struct cxl_feat_entry {
40+
uuid_t uuid;
41+
__le16 id;
42+
__le16 get_feat_size;
43+
__le16 set_feat_size;
44+
__le32 flags;
45+
u8 get_feat_ver;
46+
u8 set_feat_ver;
47+
__le16 effects;
48+
u8 reserved[18];
49+
} __packed;
50+
51+
/* @flags field for 'struct cxl_feat_entry' */
52+
#define CXL_FEATURE_F_CHANGEABLE BIT(0)
53+
#define CXL_FEATURE_F_PERSIST_FW_UPDATE BIT(4)
54+
#define CXL_FEATURE_F_DEFAULT_SEL BIT(5)
55+
#define CXL_FEATURE_F_SAVED_SEL BIT(6)
56+
57+
/*
58+
* CXL spec r3.2 Table 8-108
59+
* Get supported Features Output Payload
60+
*/
61+
struct cxl_mbox_get_sup_feats_out {
62+
__struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* no attrs */,
63+
__le16 num_entries;
64+
__le16 supported_feats;
65+
__u8 reserved[4];
66+
);
67+
struct cxl_feat_entry ents[] __counted_by_le(num_entries);
68+
} __packed;
69+
70+
/**
71+
* struct cxl_features_state - The Features state for the device
72+
* @cxlds: Pointer to CXL device state
73+
* @entries: CXl feature entry context
74+
* @num_features: total Features supported by the device
75+
* @ent: Flex array of Feature detail entries from the device
76+
*/
77+
struct cxl_features_state {
78+
struct cxl_dev_state *cxlds;
79+
struct cxl_feat_entries {
80+
int num_features;
81+
struct cxl_feat_entry ent[] __counted_by(num_features);
82+
} *entries;
83+
};
84+
85+
#ifdef CONFIG_CXL_FEATURES
86+
inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds);
87+
int devm_cxl_setup_features(struct cxl_dev_state *cxlds);
88+
#else
89+
static inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
90+
{
91+
return NULL;
92+
}
93+
94+
static inline int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
95+
{
96+
return -EOPNOTSUPP;
97+
}
98+
#endif
99+
13100
#endif

tools/testing/cxl/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
6363
cxl_core-y += $(CXL_CORE_SRC)/cdat.o
6464
cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
6565
cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
66+
cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
6667
cxl_core-y += config_check.o
6768
cxl_core-y += cxl_core_test.o
6869
cxl_core-y += cxl_core_exports.o

tools/testing/cxl/test/mem.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
15581558
if (rc)
15591559
return rc;
15601560

1561+
rc = devm_cxl_setup_features(cxlds);
1562+
if (rc)
1563+
dev_dbg(dev, "No CXL Features discovered\n");
1564+
15611565
cxl_mock_add_event_logs(&mdata->mes);
15621566

15631567
cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);

0 commit comments

Comments
 (0)