Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cxl/region: check interleave capability

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: 384e624bb211 ("cxl/region: Attach endpoint decoders")
Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://patch.msgid.link/20240614084755.59503-2-yaoxt.fnst@fujitsu.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>

authored by

Yao Xingtao and committed by
Dave Jiang
84328c5a 285f2a08

+111
+13
drivers/cxl/core/hdm.c
··· 52 52 struct cxl_dport *dport = NULL; 53 53 int single_port_map[1]; 54 54 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; 55 63 56 64 cxlsd = cxl_switch_decoder_alloc(port, 1); 57 65 if (IS_ERR(cxlsd)) ··· 87 79 cxlhdm->interleave_mask |= GENMASK(11, 8); 88 80 if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap)) 89 81 cxlhdm->interleave_mask |= GENMASK(14, 12); 82 + cxlhdm->iw_cap_mask = BIT(1) | BIT(2) | BIT(4) | BIT(8); 83 + if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY, hdm_cap)) 84 + cxlhdm->iw_cap_mask |= BIT(3) | BIT(6) | BIT(12); 85 + if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_16_WAY, hdm_cap)) 86 + cxlhdm->iw_cap_mask |= BIT(16); 90 87 } 91 88 92 89 static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
+82
drivers/cxl/core/region.c
··· 1101 1101 } 1102 1102 cxld = cxl_rr->decoder; 1103 1103 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 + 1104 1124 rc = cxl_rr_ep_add(cxl_rr, cxled); 1105 1125 if (rc) { 1106 1126 dev_dbg(&cxlr->dev, ··· 1226 1206 dev_name(&cxled_peer->cxld.dev)); 1227 1207 return -ENXIO; 1228 1208 } 1209 + 1210 + return 0; 1211 + } 1212 + 1213 + static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig) 1214 + { 1215 + struct cxl_port *port = to_cxl_port(cxld->dev.parent); 1216 + struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); 1217 + unsigned int interleave_mask; 1218 + u8 eiw; 1219 + u16 eig; 1220 + int high_pos, low_pos; 1221 + 1222 + if (!test_bit(iw, &cxlhdm->iw_cap_mask)) 1223 + return -ENXIO; 1224 + /* 1225 + * Per CXL specification r3.1(8.2.4.20.13 Decoder Protection), 1226 + * if eiw < 8: 1227 + * DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + 8 + eiw] 1228 + * DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0] 1229 + * 1230 + * when the eiw is 0, all the bits of HPAOFFSET[51: 0] are used, the 1231 + * interleave bits are none. 1232 + * 1233 + * if eiw >= 8: 1234 + * DPAOFFSET[51: eig + 8] = HPAOFFSET[51: eig + eiw] / 3 1235 + * DPAOFFSET[eig + 7: 0] = HPAOFFSET[eig + 7: 0] 1236 + * 1237 + * when the eiw is 8, all the bits of HPAOFFSET[51: 0] are used, the 1238 + * interleave bits are none. 1239 + */ 1240 + ways_to_eiw(iw, &eiw); 1241 + if (eiw == 0 || eiw == 8) 1242 + return 0; 1243 + 1244 + granularity_to_eig(ig, &eig); 1245 + if (eiw > 8) 1246 + high_pos = eiw + eig - 1; 1247 + else 1248 + high_pos = eiw + eig + 7; 1249 + low_pos = eig + 8; 1250 + interleave_mask = GENMASK(high_pos, low_pos); 1251 + if (interleave_mask & ~cxlhdm->interleave_mask) 1252 + return -ENXIO; 1229 1253 1230 1254 return 0; 1231 1255 } ··· 1424 1360 return -ENXIO; 1425 1361 } 1426 1362 } else { 1363 + rc = check_interleave_cap(cxld, iw, ig); 1364 + if (rc) { 1365 + dev_dbg(&cxlr->dev, 1366 + "%s:%s iw: %d ig: %d is not supported\n", 1367 + dev_name(port->uport_dev), 1368 + dev_name(&port->dev), iw, ig); 1369 + return rc; 1370 + } 1371 + 1427 1372 cxld->interleave_ways = iw; 1428 1373 cxld->interleave_granularity = ig; 1429 1374 cxld->hpa_range = (struct range) { ··· 1868 1795 struct cxl_port *ep_port, *root_port; 1869 1796 struct cxl_dport *dport; 1870 1797 int rc = -ENXIO; 1798 + 1799 + rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, 1800 + p->interleave_granularity); 1801 + if (rc) { 1802 + dev_dbg(&cxlr->dev, "%s iw: %d ig: %d is not supported\n", 1803 + dev_name(&cxled->cxld.dev), p->interleave_ways, 1804 + p->interleave_granularity); 1805 + return rc; 1806 + } 1871 1807 1872 1808 if (cxled->mode != cxlr->mode) { 1873 1809 dev_dbg(&cxlr->dev, "%s region mode: %d mismatch: %d\n",
+2
drivers/cxl/cxl.h
··· 47 47 #define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4) 48 48 #define CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8) 49 49 #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) 50 52 #define CXL_HDM_DECODER_CTRL_OFFSET 0x4 51 53 #define CXL_HDM_DECODER_ENABLE BIT(1) 52 54 #define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)
+10
drivers/cxl/cxlmem.h
··· 851 851 852 852 int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd); 853 853 854 + /** 855 + * struct cxl_hdm - HDM Decoder registers and cached / decoded capabilities 856 + * @regs: mapped registers, see devm_cxl_setup_hdm() 857 + * @decoder_count: number of decoders for this port 858 + * @target_count: for switch decoders, max downstream port targets 859 + * @interleave_mask: interleave granularity capability, see check_interleave_cap() 860 + * @iw_cap_mask: bitmask of supported interleave ways, see check_interleave_cap() 861 + * @port: mapped cxl_port, see devm_cxl_setup_hdm() 862 + */ 854 863 struct cxl_hdm { 855 864 struct cxl_component_regs regs; 856 865 unsigned int decoder_count; 857 866 unsigned int target_count; 858 867 unsigned int interleave_mask; 868 + unsigned long iw_cap_mask; 859 869 struct cxl_port *port; 860 870 }; 861 871
+4
tools/testing/cxl/test/cxl.c
··· 630 630 struct cxl_endpoint_dvsec_info *info) 631 631 { 632 632 struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL); 633 + struct device *dev = &port->dev; 633 634 634 635 if (!cxlhdm) 635 636 return ERR_PTR(-ENOMEM); 636 637 637 638 cxlhdm->port = port; 639 + cxlhdm->interleave_mask = ~0U; 640 + cxlhdm->iw_cap_mask = ~0UL; 641 + dev_set_drvdata(dev, cxlhdm); 638 642 return cxlhdm; 639 643 } 640 644