Add sysfs attributes to enable/disable CXL isolation and transaction timeout. The intended use for these attributes is to disable isolation and/or timeout as part of device maintenance or hotplug. The attributes are added under a new "cxl_isolation" group on the PCIe Root Port device. Signed-off-by: Ben Cheatham <Benjamin.Cheatham@xxxxxxx> --- drivers/cxl/core/port.c | 29 ++++++ drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.h | 4 + drivers/pci/pcie/cxl_isolation.c | 158 +++++++++++++++++++++++++++++++ include/cxl/isolation.h | 8 ++ 5 files changed, 202 insertions(+) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 6591e83e719c..b5a306341bb2 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -1346,6 +1346,7 @@ static int cxl_dport_enable_timeout_isolation(struct device *host, if (!(cap & CXL_ISOLATION_CAP_MEM_TIME_SUPP)) cxl_enable_timeout(dport); + pcie_update_cxl_isolation_group(dport->dport_dev); return 0; } @@ -1598,6 +1599,34 @@ struct cxl_port *find_cxl_port(struct device *dport_dev, return port; } +/** + * cxl_find_pcie_rp - Find CXL port that contains a CXL-capable PCIe Root Port + * @dport_dev: CXL-capable PCIe Root Port device + * @rp: Pointer to store found dport in + * + * Returns CXL port with elevated reference count if @dport_dev is found + */ +struct cxl_port *cxl_find_pcie_rp(struct pci_dev *dport_dev, + struct cxl_dport **rp) +{ + struct cxl_dport *dport; + struct cxl_port *parent; + + struct cxl_port *hb __free(put_cxl_port) = + find_cxl_port(&dport_dev->dev, &dport); + if (!hb || !dport) + return NULL; + + parent = parent_port_of(hb); + if (!parent || !is_cxl_root(parent)) + return NULL; + + if (rp) + *rp = dport; + + return_ptr(hb); +} + static struct cxl_port *find_cxl_port_at(struct cxl_port *parent_port, struct device *dport_dev, struct cxl_dport **dport) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 268c69daa4d5..86e8d8d918cf 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1815,6 +1815,9 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCI_DOE &pci_doe_sysfs_group, +#endif +#ifdef CONFIG_CXL_ISOLATION + &cxl_isolation_attr_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index c7fc86d93bea..3510a75c880b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -677,6 +677,10 @@ static inline void pci_rcec_exit(struct pci_dev *dev) { } static inline void pcie_link_rcec(struct pci_dev *rcec) { } #endif +#ifdef CONFIG_CXL_ISOLATION +extern struct attribute_group cxl_isolation_attr_group; +#endif + #ifdef CONFIG_PCI_ATS /* Address Translation Service */ void pci_ats_init(struct pci_dev *dev); diff --git a/drivers/pci/pcie/cxl_isolation.c b/drivers/pci/pcie/cxl_isolation.c index 5a56a327b599..9d2ad14810e8 100644 --- a/drivers/pci/pcie/cxl_isolation.c +++ b/drivers/pci/pcie/cxl_isolation.c @@ -77,6 +77,164 @@ pcie_cxl_dport_get_isolation_info(struct pci_dev *dport_dev) return get_service_data(to_pcie_device(dev)); } +static ssize_t isolation_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct cxl_port *port; + bool enable; + int rc; + + rc = kstrtobool(buf, &enable); + if (rc) + return rc; + + struct cxl_dport **dport __free(kfree) = + kzalloc(sizeof(*dport), GFP_KERNEL); + if (!dport) + return -ENOMEM; + + port = cxl_find_pcie_rp(pdev, dport); + if (!port || !(*dport)) + return -ENODEV; + + if (enable) + cxl_enable_isolation(*dport); + else + rc = cxl_disable_isolation(*dport); + + put_device(&port->dev); + return rc ? rc : n; +} + +static ssize_t isolation_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct cxl_port *port; + u32 ctrl; + + struct cxl_dport **dport __free(kfree) = + kzalloc(sizeof(*dport), GFP_KERNEL); + if (!dport) + return -ENOMEM; + + port = cxl_find_pcie_rp(pdev, dport); + if (!port || !(*dport)) + return -ENODEV; + + if (!(*dport)->regs.isolation) + return -ENXIO; + + ctrl = readl((*dport)->regs.isolation + CXL_ISOLATION_CTRL_OFFSET); + put_device(&port->dev); + + return sysfs_emit(buf, "%lu\n", + FIELD_GET(CXL_ISOLATION_CTRL_MEM_ISO_ENABLE, ctrl)); +} +DEVICE_ATTR_RW(isolation_ctrl); + +static ssize_t timeout_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct cxl_port *port; + bool enable; + int rc; + + rc = kstrtobool(buf, &enable); + if (rc) + return rc; + + struct cxl_dport **dport __free(kfree) = + kzalloc(sizeof(*dport), GFP_KERNEL); + if (!dport) + return -ENOMEM; + + port = cxl_find_pcie_rp(pdev, dport); + if (!port || !(*dport)) + return -ENODEV; + + if (enable) + cxl_enable_timeout(*dport); + else + cxl_disable_timeout(*dport); + + put_device(&port->dev); + return n; +} + +static ssize_t timeout_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct cxl_port *port; + u32 ctrl; + + struct cxl_dport **dport __free(kfree) = + kzalloc(sizeof(*dport), GFP_KERNEL); + if (!dport) + return -ENOMEM; + + port = cxl_find_pcie_rp(pdev, dport); + if (!port || !(*dport)) + return -ENODEV; + + if (!(*dport)->regs.isolation) + return -ENXIO; + + ctrl = readl((*dport)->regs.isolation + CXL_ISOLATION_CTRL_OFFSET); + put_device(&port->dev); + + return sysfs_emit(buf, "%lu\n", + FIELD_GET(CXL_ISOLATION_CTRL_MEM_TIME_ENABLE, ctrl)); +} +DEVICE_ATTR_RW(timeout_ctrl); + +static struct attribute *isolation_attrs[] = { + &dev_attr_timeout_ctrl.attr, + &dev_attr_isolation_ctrl.attr, + NULL, +}; + +static umode_t cxl_isolation_attrs_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (!pcie_is_cxl(pdev) || pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) + return 0; + + if (pcie_port_find_device(pdev, PCIE_PORT_SERVICE_CXLISO)) + return a->mode; + return 0; +} + +const struct attribute_group cxl_isolation_attr_group = { + .name = "cxl_isolation", + .attrs = isolation_attrs, + .is_visible = cxl_isolation_attrs_visible, +}; + +void +pcie_update_cxl_isolation_group(struct device *dport_dev) +{ + struct device *dev; + + if (!dev_is_pci(dport_dev)) + return; + + dev = pcie_port_find_device(to_pci_dev(dport_dev), + PCIE_PORT_SERVICE_CXLISO); + if (!dev) + return; + + sysfs_update_group(&dport_dev->kobj, &cxl_isolation_attr_group); +} + static int cxl_isolation_probe(struct pcie_device *dev) { struct cxl_isolation_info *info; diff --git a/include/cxl/isolation.h b/include/cxl/isolation.h index 73282ac262a6..0b6e4f0160a8 100644 --- a/include/cxl/isolation.h +++ b/include/cxl/isolation.h @@ -31,21 +31,29 @@ int cxl_disable_isolation(struct cxl_dport *dport); void cxl_enable_timeout(struct cxl_dport *dport); void cxl_disable_timeout(struct cxl_dport *dport); +struct cxl_port *cxl_find_pcie_rp(struct pci_dev *pdev, + struct cxl_dport **dport); #else /* !CONFIG_CXL_BUS */ static inline void cxl_enable_isolation(struct cxl_dport *dport) {} static inline int cxl_disable_isolation(struct cxl_dport *dport) { return -ENXIO; } static inline void cxl_enable_timeout(struct cxl_dport *dport) {} static inline void cxl_disable_timeout(struct cxl_dport *dport) {} + +static inline struct cxl_port *cxl_find_pcie_rp(struct pci_dev *pdev, + struct cxl_dport **dport); +{ return NULL; } #endif /* !CONFIG_CXL_BUS */ #ifdef CONFIG_CXL_ISOLATION struct cxl_isolation_info * pcie_cxl_dport_get_isolation_info(struct pci_dev *dport_dev); +void pcie_update_cxl_isolation_group(struct device *dport_dev); #else /* !CONFIG_CXL_ISOLATION */ static inline struct cxl_isolation_info * pcie_cxl_dport_get_isolation_info(struct pci_dev *dport_dev) { return NULL; } +static inline void pcie_update_cxl_isolation_group(struct device *dport_dev) {} #endif /* !CONFIG_CXL_ISOLATION */ #endif -- 2.34.1