Skip to content

Commit 73e9315

Browse files
committed
Merge tag 'cxl-fixes-6.10-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl
Pull cxl fixes from Dave Jiang: - Fix no cxl_nvd during pmem region auto-assemble - Avoid NULLL pointer dereference in region lookup - Add missing checks to interleave capability - Add cxl kdoc fix to address document compilation error * tag 'cxl-fixes-6.10-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: cxl: documentation: add missing files to cxl driver-api cxl/region: check interleave capability cxl/region: Avoid null pointer dereference in region lookup cxl/mem: Fix no cxl_nvd during pmem region auto-assembling
2 parents cfbc0ff + a0f39d5 commit 73e9315

File tree

8 files changed

+170
-25
lines changed

8 files changed

+170
-25
lines changed

Documentation/driver-api/cxl/memory-devices.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ CXL Memory Device
328328
.. kernel-doc:: drivers/cxl/mem.c
329329
:doc: cxl mem
330330

331+
.. kernel-doc:: drivers/cxl/cxlmem.h
332+
:internal:
333+
334+
.. kernel-doc:: drivers/cxl/core/memdev.c
335+
:identifiers:
336+
331337
CXL Port
332338
--------
333339
.. kernel-doc:: drivers/cxl/port.c
@@ -341,6 +347,15 @@ CXL Core
341347
.. kernel-doc:: drivers/cxl/cxl.h
342348
:internal:
343349

350+
.. kernel-doc:: drivers/cxl/core/hdm.c
351+
:doc: cxl core hdm
352+
353+
.. kernel-doc:: drivers/cxl/core/hdm.c
354+
:identifiers:
355+
356+
.. kernel-doc:: drivers/cxl/core/cdat.c
357+
:identifiers:
358+
344359
.. kernel-doc:: drivers/cxl/core/port.c
345360
:doc: cxl core
346361

drivers/cxl/core/hdm.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ int devm_cxl_add_passthrough_decoder(struct cxl_port *port)
5252
struct cxl_dport *dport = NULL;
5353
int single_port_map[1];
5454
unsigned long index;
55+
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
56+
57+
/*
58+
* Capability checks are moot for passthrough decoders, support
59+
* any and all possibilities.
60+
*/
61+
cxlhdm->interleave_mask = ~0U;
62+
cxlhdm->iw_cap_mask = ~0UL;
5563

5664
cxlsd = cxl_switch_decoder_alloc(port, 1);
5765
if (IS_ERR(cxlsd))
@@ -79,6 +87,11 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
7987
cxlhdm->interleave_mask |= GENMASK(11, 8);
8088
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap))
8189
cxlhdm->interleave_mask |= GENMASK(14, 12);
90+
cxlhdm->iw_cap_mask = BIT(1) | BIT(2) | BIT(4) | BIT(8);
91+
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY, hdm_cap))
92+
cxlhdm->iw_cap_mask |= BIT(3) | BIT(6) | BIT(12);
93+
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_16_WAY, hdm_cap))
94+
cxlhdm->iw_cap_mask |= BIT(16);
8295
}
8396

8497
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)

drivers/cxl/core/pmem.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,14 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
6262
return is_cxl_nvdimm_bridge(dev);
6363
}
6464

65-
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd)
65+
/**
66+
* cxl_find_nvdimm_bridge() - find a bridge device relative to a port
67+
* @port: any descendant port of an nvdimm-bridge associated
68+
* root-cxl-port
69+
*/
70+
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port)
6671
{
67-
struct cxl_root *cxl_root __free(put_cxl_root) =
68-
find_cxl_root(cxlmd->endpoint);
72+
struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
6973
struct device *dev;
7074

7175
if (!cxl_root)
@@ -242,18 +246,20 @@ static void cxlmd_release_nvdimm(void *_cxlmd)
242246

243247
/**
244248
* devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
249+
* @parent_port: parent port for the (to be added) @cxlmd endpoint port
245250
* @cxlmd: cxl_memdev instance that will perform LIBNVDIMM operations
246251
*
247252
* Return: 0 on success negative error code on failure.
248253
*/
249-
int devm_cxl_add_nvdimm(struct cxl_memdev *cxlmd)
254+
int devm_cxl_add_nvdimm(struct cxl_port *parent_port,
255+
struct cxl_memdev *cxlmd)
250256
{
251257
struct cxl_nvdimm_bridge *cxl_nvb;
252258
struct cxl_nvdimm *cxl_nvd;
253259
struct device *dev;
254260
int rc;
255261

256-
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd);
262+
cxl_nvb = cxl_find_nvdimm_bridge(parent_port);
257263
if (!cxl_nvb)
258264
return -ENODEV;
259265

drivers/cxl/core/region.c

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,26 @@ static int cxl_port_attach_region(struct cxl_port *port,
11011101
}
11021102
cxld = cxl_rr->decoder;
11031103

1104+
/*
1105+
* the number of targets should not exceed the target_count
1106+
* of the decoder
1107+
*/
1108+
if (is_switch_decoder(&cxld->dev)) {
1109+
struct cxl_switch_decoder *cxlsd;
1110+
1111+
cxlsd = to_cxl_switch_decoder(&cxld->dev);
1112+
if (cxl_rr->nr_targets > cxlsd->nr_targets) {
1113+
dev_dbg(&cxlr->dev,
1114+
"%s:%s %s add: %s:%s @ %d overflows targets: %d\n",
1115+
dev_name(port->uport_dev), dev_name(&port->dev),
1116+
dev_name(&cxld->dev), dev_name(&cxlmd->dev),
1117+
dev_name(&cxled->cxld.dev), pos,
1118+
cxlsd->nr_targets);
1119+
rc = -ENXIO;
1120+
goto out_erase;
1121+
}
1122+
}
1123+
11041124
rc = cxl_rr_ep_add(cxl_rr, cxled);
11051125
if (rc) {
11061126
dev_dbg(&cxlr->dev,
@@ -1210,6 +1230,50 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
12101230
return 0;
12111231
}
12121232

1233+
static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig)
1234+
{
1235+
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
1236+
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
1237+
unsigned int interleave_mask;
1238+
u8 eiw;
1239+
u16 eig;
1240+
int high_pos, low_pos;
1241+
1242+
if (!test_bit(iw, &cxlhdm->iw_cap_mask))
1243+
return -ENXIO;
1244+
/*
1245+
* Per CXL specification r3.1(8.2.4.20.13 Decoder Protection),
1246+
* if eiw < 8:
1247+
* DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + 8 + eiw]
1248+
* DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0]
1249+
*
1250+
* when the eiw is 0, all the bits of HPAOFFSET[51: 0] are used, the
1251+
* interleave bits are none.
1252+
*
1253+
* if eiw >= 8:
1254+
* DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + eiw] / 3
1255+
* DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0]
1256+
*
1257+
* when the eiw is 8, all the bits of HPAOFFSET[51: 0] are used, the
1258+
* interleave bits are none.
1259+
*/
1260+
ways_to_eiw(iw, &eiw);
1261+
if (eiw == 0 || eiw == 8)
1262+
return 0;
1263+
1264+
granularity_to_eig(ig, &eig);
1265+
if (eiw > 8)
1266+
high_pos = eiw + eig - 1;
1267+
else
1268+
high_pos = eiw + eig + 7;
1269+
low_pos = eig + 8;
1270+
interleave_mask = GENMASK(high_pos, low_pos);
1271+
if (interleave_mask & ~cxlhdm->interleave_mask)
1272+
return -ENXIO;
1273+
1274+
return 0;
1275+
}
1276+
12131277
static int cxl_port_setup_targets(struct cxl_port *port,
12141278
struct cxl_region *cxlr,
12151279
struct cxl_endpoint_decoder *cxled)
@@ -1360,6 +1424,15 @@ static int cxl_port_setup_targets(struct cxl_port *port,
13601424
return -ENXIO;
13611425
}
13621426
} else {
1427+
rc = check_interleave_cap(cxld, iw, ig);
1428+
if (rc) {
1429+
dev_dbg(&cxlr->dev,
1430+
"%s:%s iw: %d ig: %d is not supported\n",
1431+
dev_name(port->uport_dev),
1432+
dev_name(&port->dev), iw, ig);
1433+
return rc;
1434+
}
1435+
13631436
cxld->interleave_ways = iw;
13641437
cxld->interleave_granularity = ig;
13651438
cxld->hpa_range = (struct range) {
@@ -1796,6 +1869,15 @@ static int cxl_region_attach(struct cxl_region *cxlr,
17961869
struct cxl_dport *dport;
17971870
int rc = -ENXIO;
17981871

1872+
rc = check_interleave_cap(&cxled->cxld, p->interleave_ways,
1873+
p->interleave_granularity);
1874+
if (rc) {
1875+
dev_dbg(&cxlr->dev, "%s iw: %d ig: %d is not supported\n",
1876+
dev_name(&cxled->cxld.dev), p->interleave_ways,
1877+
p->interleave_granularity);
1878+
return rc;
1879+
}
1880+
17991881
if (cxled->mode != cxlr->mode) {
18001882
dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
18011883
dev_name(&cxled->cxld.dev), cxlr->mode, cxled->mode);
@@ -2688,22 +2770,33 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg)
26882770
{
26892771
struct cxl_dpa_to_region_context *ctx = arg;
26902772
struct cxl_endpoint_decoder *cxled;
2773+
struct cxl_region *cxlr;
26912774
u64 dpa = ctx->dpa;
26922775

26932776
if (!is_endpoint_decoder(dev))
26942777
return 0;
26952778

26962779
cxled = to_cxl_endpoint_decoder(dev);
2697-
if (!cxled->dpa_res || !resource_size(cxled->dpa_res))
2780+
if (!cxled || !cxled->dpa_res || !resource_size(cxled->dpa_res))
26982781
return 0;
26992782

27002783
if (dpa > cxled->dpa_res->end || dpa < cxled->dpa_res->start)
27012784
return 0;
27022785

2703-
dev_dbg(dev, "dpa:0x%llx mapped in region:%s\n", dpa,
2704-
dev_name(&cxled->cxld.region->dev));
2786+
/*
2787+
* Stop the region search (return 1) when an endpoint mapping is
2788+
* found. The region may not be fully constructed so offering
2789+
* the cxlr in the context structure is not guaranteed.
2790+
*/
2791+
cxlr = cxled->cxld.region;
2792+
if (cxlr)
2793+
dev_dbg(dev, "dpa:0x%llx mapped in region:%s\n", dpa,
2794+
dev_name(&cxlr->dev));
2795+
else
2796+
dev_dbg(dev, "dpa:0x%llx mapped in endpoint:%s\n", dpa,
2797+
dev_name(dev));
27052798

2706-
ctx->cxlr = cxled->cxld.region;
2799+
ctx->cxlr = cxlr;
27072800

27082801
return 1;
27092802
}
@@ -2847,7 +2940,7 @@ static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
28472940
* bridge for one device is the same for all.
28482941
*/
28492942
if (i == 0) {
2850-
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd);
2943+
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
28512944
if (!cxl_nvb)
28522945
return -ENODEV;
28532946
cxlr->cxl_nvb = cxl_nvb;

drivers/cxl/cxl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
4747
#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
4848
#define CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
4949
#define CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
50+
#define CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY BIT(11)
51+
#define CXL_HDM_DECODER_INTERLEAVE_16_WAY BIT(12)
5052
#define CXL_HDM_DECODER_CTRL_OFFSET 0x4
5153
#define CXL_HDM_DECODER_ENABLE BIT(1)
5254
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)
@@ -855,8 +857,8 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
855857
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
856858
bool is_cxl_nvdimm(struct device *dev);
857859
bool is_cxl_nvdimm_bridge(struct device *dev);
858-
int devm_cxl_add_nvdimm(struct cxl_memdev *cxlmd);
859-
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd);
860+
int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd);
861+
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);
860862

861863
#ifdef CONFIG_CXL_REGION
862864
bool is_cxl_pmem_region(struct device *dev);

drivers/cxl/cxlmem.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,9 @@ enum cxl_devtype {
395395

396396
/**
397397
* struct cxl_dpa_perf - DPA performance property entry
398-
* @dpa_range - range for DPA address
399-
* @coord - QoS performance data (i.e. latency, bandwidth)
400-
* @qos_class - QoS Class cookies
398+
* @dpa_range: range for DPA address
399+
* @coord: QoS performance data (i.e. latency, bandwidth)
400+
* @qos_class: QoS Class cookies
401401
*/
402402
struct cxl_dpa_perf {
403403
struct range dpa_range;
@@ -464,13 +464,14 @@ struct cxl_dev_state {
464464
* @active_persistent_bytes: sum of hard + soft persistent
465465
* @next_volatile_bytes: volatile capacity change pending device reset
466466
* @next_persistent_bytes: persistent capacity change pending device reset
467+
* @ram_perf: performance data entry matched to RAM partition
468+
* @pmem_perf: performance data entry matched to PMEM partition
467469
* @event: event log driver state
468470
* @poison: poison driver state info
469471
* @security: security driver state info
470472
* @fw: firmware upload / activation state
473+
* @mbox_wait: RCU wait for mbox send completely
471474
* @mbox_send: @dev specific transport for transmitting mailbox commands
472-
* @ram_perf: performance data entry matched to RAM partition
473-
* @pmem_perf: performance data entry matched to PMEM partition
474475
*
475476
* See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for
476477
* details on capacity parameters.
@@ -851,11 +852,21 @@ static inline void cxl_mem_active_dec(void)
851852

852853
int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
853854

855+
/**
856+
* struct cxl_hdm - HDM Decoder registers and cached / decoded capabilities
857+
* @regs: mapped registers, see devm_cxl_setup_hdm()
858+
* @decoder_count: number of decoders for this port
859+
* @target_count: for switch decoders, max downstream port targets
860+
* @interleave_mask: interleave granularity capability, see check_interleave_cap()
861+
* @iw_cap_mask: bitmask of supported interleave ways, see check_interleave_cap()
862+
* @port: mapped cxl_port, see devm_cxl_setup_hdm()
863+
*/
854864
struct cxl_hdm {
855865
struct cxl_component_regs regs;
856866
unsigned int decoder_count;
857867
unsigned int target_count;
858868
unsigned int interleave_mask;
869+
unsigned long iw_cap_mask;
859870
struct cxl_port *port;
860871
};
861872

drivers/cxl/mem.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ static int cxl_mem_probe(struct device *dev)
152152
return -ENXIO;
153153
}
154154

155+
if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) {
156+
rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
157+
if (rc) {
158+
if (rc == -ENODEV)
159+
dev_info(dev, "PMEM disabled by platform\n");
160+
return rc;
161+
}
162+
}
163+
155164
if (dport->rch)
156165
endpoint_parent = parent_port->uport_dev;
157166
else
@@ -174,14 +183,6 @@ static int cxl_mem_probe(struct device *dev)
174183
if (rc)
175184
return rc;
176185

177-
if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) {
178-
rc = devm_cxl_add_nvdimm(cxlmd);
179-
if (rc == -ENODEV)
180-
dev_info(dev, "PMEM disabled by platform\n");
181-
else
182-
return rc;
183-
}
184-
185186
/*
186187
* The kernel may be operating out of CXL memory on this device,
187188
* there is no spec defined way to determine whether this device

tools/testing/cxl/test/cxl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,11 +630,15 @@ static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port,
630630
struct cxl_endpoint_dvsec_info *info)
631631
{
632632
struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
633+
struct device *dev = &port->dev;
633634

634635
if (!cxlhdm)
635636
return ERR_PTR(-ENOMEM);
636637

637638
cxlhdm->port = port;
639+
cxlhdm->interleave_mask = ~0U;
640+
cxlhdm->iw_cap_mask = ~0UL;
641+
dev_set_drvdata(dev, cxlhdm);
638642
return cxlhdm;
639643
}
640644

0 commit comments

Comments
 (0)