Skip to content

Commit c1bd23f

Browse files
sailer1205smb49
authored andcommitted
cxl/region: check interleave capability
BugLink: https://bugs.launchpad.net/bugs/2076435 [ Upstream commit 84328c5 ] Since interleave capability is not verified, if the interleave capability of a target does not match the region need, committing decoder should have failed at the device end. In order to checkout this error as quickly as possible, driver needs to check the interleave capability of target during attaching it to region. Per CXL specification r3.1(8.2.4.20.1 CXL HDM Decoder Capability Register), bits 11 and 12 indicate the capability to establish interleaving in 3, 6, 12 and 16 ways. If these bits are not set, the target cannot be attached to a region utilizing such interleave ways. Additionally, bits 8 and 9 represent the capability of the bits used for interleaving in the address, Linux tracks this in the cxl_port interleave_mask. Per CXL specification r3.1(8.2.4.20.13 Decoder Protection): eIW means encoded Interleave Ways. eIG means encoded Interleave Granularity. in HPA: if eIW is 0 or 8 (interleave ways: 1, 3), all the bits of HPA are used, the interleave bits are none, the following check is ignored. if eIW is less than 8 (interleave ways: 2, 4, 8, 16), the interleave bits start at bit position eIG + 8 and end at eIG + eIW + 8 - 1. if eIW is greater than 8 (interleave ways: 6, 12), the interleave bits start at bit position eIG + 8 and end at eIG + eIW - 1. if the interleave mask is insufficient to cover the required interleave bits, the target cannot be attached to the region. Fixes: 384e624 ("cxl/region: Attach endpoint decoders") Signed-off-by: Yao Xingtao <[email protected]> Reviewed-by: Dan Williams <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Dave Jiang <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Portia Stephens <[email protected]> Signed-off-by: Roxana Nicolescu <[email protected]>
1 parent 60073ea commit c1bd23f

File tree

5 files changed

+111
-0
lines changed

5 files changed

+111
-0
lines changed

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/region.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,26 @@ static int cxl_port_attach_region(struct cxl_port *port,
998998
}
999999
cxld = cxl_rr->decoder;
10001000

1001+
/*
1002+
* the number of targets should not exceed the target_count
1003+
* of the decoder
1004+
*/
1005+
if (is_switch_decoder(&cxld->dev)) {
1006+
struct cxl_switch_decoder *cxlsd;
1007+
1008+
cxlsd = to_cxl_switch_decoder(&cxld->dev);
1009+
if (cxl_rr->nr_targets > cxlsd->nr_targets) {
1010+
dev_dbg(&cxlr->dev,
1011+
"%s:%s %s add: %s:%s @ %d overflows targets: %d\n",
1012+
dev_name(port->uport_dev), dev_name(&port->dev),
1013+
dev_name(&cxld->dev), dev_name(&cxlmd->dev),
1014+
dev_name(&cxled->cxld.dev), pos,
1015+
cxlsd->nr_targets);
1016+
rc = -ENXIO;
1017+
goto out_erase;
1018+
}
1019+
}
1020+
10011021
rc = cxl_rr_ep_add(cxl_rr, cxled);
10021022
if (rc) {
10031023
dev_dbg(&cxlr->dev,
@@ -1107,6 +1127,50 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
11071127
return 0;
11081128
}
11091129

1130+
static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig)
1131+
{
1132+
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
1133+
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
1134+
unsigned int interleave_mask;
1135+
u8 eiw;
1136+
u16 eig;
1137+
int high_pos, low_pos;
1138+
1139+
if (!test_bit(iw, &cxlhdm->iw_cap_mask))
1140+
return -ENXIO;
1141+
/*
1142+
* Per CXL specification r3.1(8.2.4.20.13 Decoder Protection),
1143+
* if eiw < 8:
1144+
* DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + 8 + eiw]
1145+
* DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0]
1146+
*
1147+
* when the eiw is 0, all the bits of HPAOFFSET[51: 0] are used, the
1148+
* interleave bits are none.
1149+
*
1150+
* if eiw >= 8:
1151+
* DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + eiw] / 3
1152+
* DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0]
1153+
*
1154+
* when the eiw is 8, all the bits of HPAOFFSET[51: 0] are used, the
1155+
* interleave bits are none.
1156+
*/
1157+
ways_to_eiw(iw, &eiw);
1158+
if (eiw == 0 || eiw == 8)
1159+
return 0;
1160+
1161+
granularity_to_eig(ig, &eig);
1162+
if (eiw > 8)
1163+
high_pos = eiw + eig - 1;
1164+
else
1165+
high_pos = eiw + eig + 7;
1166+
low_pos = eig + 8;
1167+
interleave_mask = GENMASK(high_pos, low_pos);
1168+
if (interleave_mask & ~cxlhdm->interleave_mask)
1169+
return -ENXIO;
1170+
1171+
return 0;
1172+
}
1173+
11101174
static int cxl_port_setup_targets(struct cxl_port *port,
11111175
struct cxl_region *cxlr,
11121176
struct cxl_endpoint_decoder *cxled)
@@ -1257,6 +1321,15 @@ static int cxl_port_setup_targets(struct cxl_port *port,
12571321
return -ENXIO;
12581322
}
12591323
} else {
1324+
rc = check_interleave_cap(cxld, iw, ig);
1325+
if (rc) {
1326+
dev_dbg(&cxlr->dev,
1327+
"%s:%s iw: %d ig: %d is not supported\n",
1328+
dev_name(port->uport_dev),
1329+
dev_name(&port->dev), iw, ig);
1330+
return rc;
1331+
}
1332+
12601333
cxld->interleave_ways = iw;
12611334
cxld->interleave_granularity = ig;
12621335
cxld->hpa_range = (struct range) {
@@ -1693,6 +1766,15 @@ static int cxl_region_attach(struct cxl_region *cxlr,
16931766
struct cxl_dport *dport;
16941767
int rc = -ENXIO;
16951768

1769+
rc = check_interleave_cap(&cxled->cxld, p->interleave_ways,
1770+
p->interleave_granularity);
1771+
if (rc) {
1772+
dev_dbg(&cxlr->dev, "%s iw: %d ig: %d is not supported\n",
1773+
dev_name(&cxled->cxld.dev), p->interleave_ways,
1774+
p->interleave_granularity);
1775+
return rc;
1776+
}
1777+
16961778
if (cxled->mode != cxlr->mode) {
16971779
dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
16981780
dev_name(&cxled->cxld.dev), cxlr->mode, cxled->mode);

drivers/cxl/cxl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
4545
#define CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
4646
#define CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
47+
#define CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY BIT(11)
48+
#define CXL_HDM_DECODER_INTERLEAVE_16_WAY BIT(12)
4749
#define CXL_HDM_DECODER_CTRL_OFFSET 0x4
4850
#define CXL_HDM_DECODER_ENABLE BIT(1)
4951
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)

drivers/cxl/cxlmem.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,11 +848,21 @@ static inline void cxl_mem_active_dec(void)
848848

849849
int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
850850

851+
/**
852+
* struct cxl_hdm - HDM Decoder registers and cached / decoded capabilities
853+
* @regs: mapped registers, see devm_cxl_setup_hdm()
854+
* @decoder_count: number of decoders for this port
855+
* @target_count: for switch decoders, max downstream port targets
856+
* @interleave_mask: interleave granularity capability, see check_interleave_cap()
857+
* @iw_cap_mask: bitmask of supported interleave ways, see check_interleave_cap()
858+
* @port: mapped cxl_port, see devm_cxl_setup_hdm()
859+
*/
851860
struct cxl_hdm {
852861
struct cxl_component_regs regs;
853862
unsigned int decoder_count;
854863
unsigned int target_count;
855864
unsigned int interleave_mask;
865+
unsigned long iw_cap_mask;
856866
struct cxl_port *port;
857867
};
858868

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)