Skip to content

Commit 4d07a05

Browse files
davejiangdjbw
authored andcommitted
cxl: Calculate and store PCI link latency for the downstream ports
The latency is calculated by dividing the flit size over the bandwidth. Add support to retrieve the flit size for the CXL switch device and calculate the latency of the PCIe link. Cache the latency number with cxl_dport. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Dave Jiang <dave.jiang@intel.com> Link: https://lore.kernel.org/r/170319621931.2212653.6800240203604822886.stgit@djiang5-mobl3 Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 7908159 commit 4d07a05

7 files changed

Lines changed: 98 additions & 2 deletions

File tree

drivers/cxl/core/core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,6 @@ enum cxl_poison_trace_type {
8888
CXL_POISON_TRACE_CLEAR,
8989
};
9090

91+
long cxl_pci_get_latency(struct pci_dev *pdev);
92+
9193
#endif /* __CXL_CORE_H__ */

drivers/cxl/core/pci.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3+
#include <linux/units.h>
34
#include <linux/io-64-nonatomic-lo-hi.h>
45
#include <linux/device.h>
56
#include <linux/delay.h>
@@ -979,3 +980,38 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
979980
return PCI_ERS_RESULT_NEED_RESET;
980981
}
981982
EXPORT_SYMBOL_NS_GPL(cxl_error_detected, CXL);
983+
984+
static int cxl_flit_size(struct pci_dev *pdev)
985+
{
986+
if (cxl_pci_flit_256(pdev))
987+
return 256;
988+
989+
return 68;
990+
}
991+
992+
/**
993+
* cxl_pci_get_latency - calculate the link latency for the PCIe link
994+
* @pdev: PCI device
995+
*
996+
* return: calculated latency or 0 for no latency
997+
*
998+
* CXL Memory Device SW Guide v1.0 2.11.4 Link latency calculation
999+
* Link latency = LinkPropagationLatency + FlitLatency + RetimerLatency
1000+
* LinkProgationLatency is negligible, so 0 will be used
1001+
* RetimerLatency is assumed to be negligible and 0 will be used
1002+
* FlitLatency = FlitSize / LinkBandwidth
1003+
* FlitSize is defined by spec. CXL rev3.0 4.2.1.
1004+
* 68B flit is used up to 32GT/s. >32GT/s, 256B flit size is used.
1005+
* The FlitLatency is converted to picoseconds.
1006+
*/
1007+
long cxl_pci_get_latency(struct pci_dev *pdev)
1008+
{
1009+
long bw;
1010+
1011+
bw = pcie_link_speed_mbps(pdev);
1012+
if (bw < 0)
1013+
return 0;
1014+
bw /= BITS_PER_BYTE;
1015+
1016+
return cxl_flit_size(pdev) * MEGA / bw;
1017+
}

drivers/cxl/core/port.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,9 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
854854
if (rc)
855855
return ERR_PTR(rc);
856856

857+
if (parent_dport && dev_is_pci(uport_dev))
858+
port->pci_latency = cxl_pci_get_latency(to_pci_dev(uport_dev));
859+
857860
return port;
858861

859862
err:
@@ -1137,6 +1140,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
11371140
if (rc)
11381141
return ERR_PTR(rc);
11391142

1143+
if (dev_is_pci(dport_dev))
1144+
dport->link_latency = cxl_pci_get_latency(to_pci_dev(dport_dev));
1145+
11401146
return dport;
11411147
}
11421148

drivers/cxl/cxl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ struct cxl_dax_region {
591591
* @depth: How deep this port is relative to the root. depth 0 is the root.
592592
* @cdat: Cached CDAT data
593593
* @cdat_available: Should a CDAT attribute be available in sysfs
594+
* @pci_latency: Upstream latency in picoseconds
594595
*/
595596
struct cxl_port {
596597
struct device dev;
@@ -613,6 +614,7 @@ struct cxl_port {
613614
size_t length;
614615
} cdat;
615616
bool cdat_available;
617+
long pci_latency;
616618
};
617619

618620
struct cxl_root_ops {
@@ -659,6 +661,7 @@ struct cxl_rcrb_info {
659661
* @port: reference to cxl_port that contains this downstream port
660662
* @regs: Dport parsed register blocks
661663
* @sw_coord: access coordinates (performance) for switch from CDAT
664+
* @link_latency: calculated PCIe downstream latency
662665
*/
663666
struct cxl_dport {
664667
struct device *dport_dev;
@@ -669,6 +672,7 @@ struct cxl_dport {
669672
struct cxl_port *port;
670673
struct cxl_regs regs;
671674
struct access_coordinate sw_coord;
675+
long link_latency;
672676
};
673677

674678
/**

drivers/cxl/cxlpci.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ struct cdat_entry_header {
8585
__le16 length;
8686
} __packed;
8787

88+
/*
89+
* CXL v3.0 6.2.3 Table 6-4
90+
* The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits
91+
* mode, otherwise it's 68B flits mode.
92+
*/
93+
static inline bool cxl_pci_flit_256(struct pci_dev *pdev)
94+
{
95+
u16 lnksta2;
96+
97+
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &lnksta2);
98+
return lnksta2 & PCI_EXP_LNKSTA2_FLIT;
99+
}
100+
88101
int devm_cxl_port_enumerate_dports(struct cxl_port *port);
89102
struct cxl_dev_state;
90103
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,

drivers/pci/pci.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6224,6 +6224,41 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
62246224
}
62256225
EXPORT_SYMBOL(pcie_set_mps);
62266226

6227+
static enum pci_bus_speed to_pcie_link_speed(u16 lnksta)
6228+
{
6229+
return pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta)];
6230+
}
6231+
6232+
int pcie_link_speed_mbps(struct pci_dev *pdev)
6233+
{
6234+
u16 lnksta;
6235+
int err;
6236+
6237+
err = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
6238+
if (err)
6239+
return err;
6240+
6241+
switch (to_pcie_link_speed(lnksta)) {
6242+
case PCIE_SPEED_2_5GT:
6243+
return 2500;
6244+
case PCIE_SPEED_5_0GT:
6245+
return 5000;
6246+
case PCIE_SPEED_8_0GT:
6247+
return 8000;
6248+
case PCIE_SPEED_16_0GT:
6249+
return 16000;
6250+
case PCIE_SPEED_32_0GT:
6251+
return 32000;
6252+
case PCIE_SPEED_64_0GT:
6253+
return 64000;
6254+
default:
6255+
break;
6256+
}
6257+
6258+
return -EINVAL;
6259+
}
6260+
EXPORT_SYMBOL(pcie_link_speed_mbps);
6261+
62276262
/**
62286263
* pcie_bandwidth_available - determine minimum link settings of a PCIe
62296264
* device and its bandwidth limitation
@@ -6257,8 +6292,7 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
62576292
while (dev) {
62586293
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
62596294

6260-
next_speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS,
6261-
lnksta)];
6295+
next_speed = to_pcie_link_speed(lnksta);
62626296
next_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
62636297

62646298
next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);

include/linux/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
13641364
u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
13651365
enum pci_bus_speed *speed,
13661366
enum pcie_link_width *width);
1367+
int pcie_link_speed_mbps(struct pci_dev *pdev);
13671368
void pcie_print_link_status(struct pci_dev *dev);
13681369
int pcie_reset_flr(struct pci_dev *dev, bool probe);
13691370
int pcie_flr(struct pci_dev *dev);

0 commit comments

Comments
 (0)