Patches #1 to #16 are a rework of the Component Register setup. This
is needed to share multiple CXL capabilities (HDM and RAS) for the
same component, also there can be different components implementing
the same capability, finally RCH mode should be supported too. The
general approach to solve this is to:
* Unify code for components and capabilities in VH and RCH modes.
* Early setup of the Component Register base address.
* Create and store the register mappings to later use it for mapping
the capability I/O ranges.
Patches #17 to #23 enable CXL RCH error handling. These are needed because
RCH downstream port protocol error handling is implemented uniquely and not
currently supported. These patches address the following:
* Discovery and mapping of RCH downstream port AER registers.
* AER portdrv changes to support CXL RCH protocol errors.
* Interrupt setup specific to RCH mode: enabling RCEC internal
errors and disabling root port interrupts.
Dan Williams (1):
cxl/rch: Prepare for caching the MMIO mapped PCIe AER capability
Robert Richter (16):
cxl/acpi: Probe RCRB later during RCH downstream port creation
cxl: Rename member @dport of struct cxl_dport to @dev
cxl/core/regs: Add @dev to cxl_register_map
cxl/acpi: Moving add_host_bridge_uport() around
cxl/acpi: Directly bind the CEDT detected CHBCR to the Host Bridge's
port
cxl/regs: Remove early capability checks in Component Register setup
cxl/pci: Early setup RCH dport component registers from RCRB
cxl/port: Store the port's Component Register mappings in struct
cxl_port
cxl/port: Store the downstream port's Component Register mappings in
struct cxl_dport
cxl/pci: Store the endpoint's Component Register mappings in struct
cxl_dev_state
cxl/hdm: Use stored Component Register mappings to map HDM decoder
capability
cxl/port: Remove Component Register base address from struct cxl_port
cxl/port: Remove Component Register base address from struct cxl_dport
cxl/pci: Remove Component Register base address from struct
cxl_dev_state
PCI/AER: Forward RCH downstream port-detected errors to the CXL.mem
dev handler
PCI/AER: Unmask RCEC internal errors to enable RCH downstream port
error handling
Terry Bowman (6):
cxl/pci: Refactor component register discovery for reuse
cxl/pci: Add RCH downstream port AER register discovery
PCI/AER: Refactor cper_print_aer() for use by CXL driver module
cxl/pci: Update CXL error logging to use RAS register address
cxl/pci: Prepare for logging RCH downstream port protocol errors
cxl/pci: Add RCH downstream port error logging
base-commit: a70fc4ed20a6118837b0aecbbf789074935f473b
drivers/cxl/acpi.c | 191 +++++++++++++++++++---------------
drivers/cxl/core/hdm.c | 59 +++++------
drivers/cxl/core/pci.c | 140 ++++++++++++++++++++++---
drivers/cxl/core/port.c | 157 ++++++++++++++++++++++++----
drivers/cxl/core/region.c | 4 +-
drivers/cxl/core/regs.c | 152 ++++++++++++++++++++++++---
drivers/cxl/cxl.h | 56 ++++++----
drivers/cxl/cxlmem.h | 5 +-
drivers/cxl/mem.c | 16 +--
drivers/cxl/pci.c | 109 +++++++------------
drivers/cxl/port.c | 5 +-
drivers/pci/pcie/Kconfig | 12 +++
drivers/pci/pcie/aer.c | 173 ++++++++++++++++++++++++++++--
include/linux/aer.h | 2 +-
tools/testing/cxl/Kbuild | 2 +-
tools/testing/cxl/test/cxl.c | 10 +-
tools/testing/cxl/test/mock.c | 12 +--
tools/testing/cxl/test/mock.h | 7 +-
18 files changed, 824 insertions(+), 288 deletions(-)
--
2.34.1
From: Robert Richter <[email protected]>
Now, that the Component Register mappings are stored, use them to
enable and map the HDM decoder capabilities. The Component Registers
do not need to be probed again for this, remove probing code.
The HDM capability applies to Endpoints, USPs and VH Host Bridges. The
Endpoint's component register mappings are located in the cxlds and
else in the port's structure. Provide a helper function
cxl_port_get_comp_map() to locate the mappings depending on the
component's type.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/hdm.c | 59 +++++++++++++++++++++---------------------
1 file changed, 30 insertions(+), 29 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 5abfa9276dac..55b5cb4842ae 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -81,26 +81,6 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
cxlhdm->interleave_mask |= GENMASK(14, 12);
}
-static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
- struct cxl_component_regs *regs)
-{
- struct cxl_register_map map = {
- .dev = &port->dev,
- .resource = port->component_reg_phys,
- .base = crb,
- .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
- };
-
- cxl_probe_component_regs(&port->dev, crb, &map.component_map);
- if (!map.component_map.hdm_decoder.valid) {
- dev_dbg(&port->dev, "HDM decoder registers not implemented\n");
- /* unique error code to indicate no HDM decoder capability */
- return -ENODEV;
- }
-
- return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
-}
-
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
{
struct cxl_hdm *cxlhdm;
@@ -145,6 +125,22 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
return true;
}
+struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
+{
+ /*
+ * HDM capability applies to Endpoints, USPs and VH Host
+ * Bridges. The Endpoint's component register mappings are
+ * located in the cxlds.
+ */
+ if (is_cxl_endpoint(port)) {
+ struct cxl_memdev *memdev = to_cxl_memdev(port->uport);
+
+ return &memdev->cxlds->comp_map;
+ }
+
+ return &port->comp_map;
+}
+
/**
* devm_cxl_setup_hdm - map HDM decoder component registers
* @port: cxl_port to map
@@ -155,7 +151,7 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
{
struct device *dev = &port->dev;
struct cxl_hdm *cxlhdm;
- void __iomem *crb;
+ struct cxl_register_map *comp_map;
int rc;
cxlhdm = devm_kzalloc(dev, sizeof(*cxlhdm), GFP_KERNEL);
@@ -164,19 +160,24 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
cxlhdm->port = port;
dev_set_drvdata(dev, cxlhdm);
- crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
- if (!crb && info && info->mem_enabled) {
- cxlhdm->decoder_count = info->ranges;
- return cxlhdm;
- } else if (!crb) {
+ comp_map = cxl_port_get_comp_map(port);
+
+ if (!comp_map->component_map.hdm_decoder.valid) {
+ dev_dbg(&port->dev, "HDM decoder registers not found\n");
+ if (info && info->mem_enabled) {
+ cxlhdm->decoder_count = info->ranges;
+ return cxlhdm;
+ }
dev_err(dev, "No component registers mapped\n");
return ERR_PTR(-ENXIO);
}
- rc = map_hdm_decoder_regs(port, crb, &cxlhdm->regs);
- iounmap(crb);
- if (rc)
+ rc = cxl_map_component_regs(comp_map, &cxlhdm->regs,
+ BIT(CXL_CM_CAP_CAP_ID_HDM));
+ if (rc) {
+ dev_dbg(dev, "Failed to map HDM capability.\n");
return ERR_PTR(rc);
+ }
parse_hdm_decoder_caps(cxlhdm);
if (cxlhdm->decoder_count == 0) {
--
2.34.1
From: Robert Richter <[email protected]>
During a Host Bridge's downstream port enumeration the CHBS entries in
the CEDT table are parsed, its Component Register base address
extracted and then stored in struct cxl_dport. The CHBS may contain
either the RCRB (RCH mode) or the Host Bridge's Component Registers
(CHBCR, VH mode). The RCRB further contains the CXL downstream port
register base address, while in VH mode the CXL Downstream Switch
Ports are visible in the PCI hierarchy and the DP's component regs are
disovered using the CXL DVSEC register locator capability. The
Component Registers derived from the CHBS for both modes are different
and thus also must be treated differently. That is, in RCH mode, the
component regs base should be bound to the dport, but in VH mode to
the CXL host bridge's port object.
The current implementation stores the CHBCR in addition in struct
cxl_dport and copies it later from there to struct cxl_port. As a
result, the dport contains the wrong Component Registers base address
and, e.g. the RAS capability of a CXL Root Port cannot be detected.
To fix the CHBCR binding, attach it directly to the Host Bridge's
@cxl_port structure. Do this during port creation of the Host Bridge
in add_host_bridge_uport(). Factor out CHBS parsing code in
add_host_bridge_dport() and use it in both functions.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/acpi.c | 65 +++++++++++++++++++++++++++++++++++-----------
1 file changed, 50 insertions(+), 15 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 4fd9fe32f830..78a24b2ca923 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -333,8 +333,8 @@ struct cxl_chbs_context {
u32 cxl_version;
};
-static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
- const unsigned long end)
+static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
+ const unsigned long end)
{
struct cxl_chbs_context *ctx = arg;
struct acpi_cedt_chbs *chbs;
@@ -362,6 +362,22 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
return 0;
}
+static int cxl_get_chbs(struct acpi_device *hb, struct cxl_chbs_context *ctx)
+{
+ unsigned long long uid;
+ int rc;
+
+ rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
+ if (rc != AE_OK)
+ return -ENOENT;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->uid = uid;
+ acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
+
+ return 0;
+}
+
static int add_host_bridge_dport(struct device *match, void *arg)
{
acpi_status rc;
@@ -377,19 +393,15 @@ static int add_host_bridge_dport(struct device *match, void *arg)
if (!hb)
return 0;
- rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
- if (rc != AE_OK) {
+ rc = cxl_get_chbs(hb, &ctx);
+ if (rc == -ENOENT)
dev_err(match, "unable to retrieve _UID\n");
- return -ENODEV;
- }
+ if (rc)
+ return rc;
+ uid = ctx.uid;
dev_dbg(match, "UID found: %lld\n", uid);
- ctx = (struct cxl_chbs_context) {
- .uid = uid,
- };
- acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
-
if (!ctx.base) {
dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
uid);
@@ -405,12 +417,17 @@ static int add_host_bridge_dport(struct device *match, void *arg)
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
+ /*
+ * In RCH mode, bind the component regs base to the dport. In
+ * VH mode it will be bound to the CXL host bridge's port
+ * object later in add_host_bridge_uport().
+ */
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
} else {
- dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
- dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
+ dport = devm_cxl_add_dport(root_port, bridge, uid,
+ CXL_RESOURCE_NONE);
}
if (IS_ERR(dport))
@@ -432,6 +449,8 @@ static int add_host_bridge_uport(struct device *match, void *arg)
struct cxl_dport *dport;
struct cxl_port *port;
struct device *bridge;
+ struct cxl_chbs_context ctx;
+ resource_size_t component_reg_phys;
int rc;
if (!hb)
@@ -450,12 +469,28 @@ static int add_host_bridge_uport(struct device *match, void *arg)
return 0;
}
+ rc = cxl_get_chbs(hb, &ctx);
+ if (rc)
+ return rc;
+
+ if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11)
+ /* RCH mode, should never happen */
+ return 0;
+
+ if (ctx.base)
+ component_reg_phys = ctx.base;
+ else
+ component_reg_phys = CXL_RESOURCE_NONE;
+
+ if (component_reg_phys != CXL_RESOURCE_NONE)
+ dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
+ ctx.uid, &component_reg_phys);
+
rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
if (rc)
return rc;
- port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
- dport);
+ port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
if (IS_ERR(port))
return PTR_ERR(port);
--
2.34.1
The endpoint implements component register setup code. Refactor it for
reuse with RCRB, downstream port, and upstream port setup.
Move PCI specifics from cxl_setup_regs() into cxl_pci_setup_regs().
Move cxl_setup_regs() into cxl/core/regs.c and export it. This also
includes supporting static functions cxl_map_registerblock(),
cxl_unmap_register_block() and cxl_probe_regs().
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/regs.c | 76 +++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 1 +
drivers/cxl/pci.c | 78 +++--------------------------------------
3 files changed, 82 insertions(+), 73 deletions(-)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 9888bdf43e55..cb2a5b1c6db5 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -336,6 +336,82 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
+static int cxl_map_regblock(struct cxl_register_map *map)
+{
+ map->base = ioremap(map->resource, map->max_size);
+ if (!map->base) {
+ dev_err(map->dev, "failed to map registers\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(map->dev, "Mapped CXL Memory Device resource %pa\n",
+ &map->resource);
+
+ return 0;
+}
+
+static void cxl_unmap_regblock(struct cxl_register_map *map)
+{
+ iounmap(map->base);
+ map->base = NULL;
+}
+
+static int cxl_probe_regs(struct cxl_register_map *map)
+{
+ struct cxl_component_reg_map *comp_map;
+ struct cxl_device_reg_map *dev_map;
+ void __iomem *base = map->base;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_COMPONENT:
+ comp_map = &map->component_map;
+ cxl_probe_component_regs(map->dev, base, comp_map);
+ if (!comp_map->hdm_decoder.valid) {
+ dev_err(map->dev, "HDM decoder registers not found\n");
+ return -ENXIO;
+ }
+
+ if (!comp_map->ras.valid)
+ dev_dbg(map->dev, "RAS registers not found\n");
+
+ dev_dbg(map->dev, "Set up component registers\n");
+ break;
+ case CXL_REGLOC_RBI_MEMDEV:
+ dev_map = &map->device_map;
+ cxl_probe_device_regs(map->dev, base, dev_map);
+ if (!dev_map->status.valid || !dev_map->mbox.valid ||
+ !dev_map->memdev.valid) {
+ dev_err(map->dev, "registers not found: %s%s%s\n",
+ !dev_map->status.valid ? "status " : "",
+ !dev_map->mbox.valid ? "mbox " : "",
+ !dev_map->memdev.valid ? "memdev " : "");
+ return -ENXIO;
+ }
+
+ dev_dbg(map->dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int cxl_setup_regs(struct cxl_register_map *map)
+{
+ int rc;
+
+ rc = cxl_map_regblock(map);
+ if (rc)
+ return rc;
+
+ rc = cxl_probe_regs(map);
+ cxl_unmap_regblock(map);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
+
resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
struct cxl_rcrb_info *ri, enum cxl_rcrb which)
{
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 095b767c21e9..1c6fe53e9dc7 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -264,6 +264,7 @@ int cxl_map_device_regs(struct cxl_register_map *map,
enum cxl_regloc_type;
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
+int cxl_setup_regs(struct cxl_register_map *map);
enum cxl_rcrb {
CXL_RCRB_DOWNSTREAM,
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 2a9f65be148b..ac17bc0430dc 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,69 +274,8 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
-static int cxl_map_regblock(struct cxl_register_map *map)
-{
- map->base = ioremap(map->resource, map->max_size);
- if (!map->base) {
- dev_err(map->dev, "failed to map registers\n");
- return -ENOMEM;
- }
-
- dev_dbg(map->dev, "Mapped CXL Memory Device resource %pa\n",
- &map->resource);
-
- return 0;
-}
-
-static void cxl_unmap_regblock(struct cxl_register_map *map)
-{
- iounmap(map->base);
- map->base = NULL;
-}
-
-static int cxl_probe_regs(struct cxl_register_map *map)
-{
- struct cxl_component_reg_map *comp_map;
- struct cxl_device_reg_map *dev_map;
- void __iomem *base = map->base;
-
- switch (map->reg_type) {
- case CXL_REGLOC_RBI_COMPONENT:
- comp_map = &map->component_map;
- cxl_probe_component_regs(map->dev, base, comp_map);
- if (!comp_map->hdm_decoder.valid) {
- dev_err(map->dev, "HDM decoder registers not found\n");
- return -ENXIO;
- }
-
- if (!comp_map->ras.valid)
- dev_dbg(map->dev, "RAS registers not found\n");
-
- dev_dbg(map->dev, "Set up component registers\n");
- break;
- case CXL_REGLOC_RBI_MEMDEV:
- dev_map = &map->device_map;
- cxl_probe_device_regs(map->dev, base, dev_map);
- if (!dev_map->status.valid || !dev_map->mbox.valid ||
- !dev_map->memdev.valid) {
- dev_err(map->dev, "registers not found: %s%s%s\n",
- !dev_map->status.valid ? "status " : "",
- !dev_map->mbox.valid ? "mbox " : "",
- !dev_map->memdev.valid ? "memdev " : "");
- return -ENXIO;
- }
-
- dev_dbg(map->dev, "Probing device registers...\n");
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
- struct cxl_register_map *map)
+static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
+ struct cxl_register_map *map)
{
int rc;
@@ -344,14 +283,7 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
if (rc)
return rc;
- rc = cxl_map_regblock(map);
- if (rc)
- return rc;
-
- rc = cxl_probe_regs(map);
- cxl_unmap_regblock(map);
-
- return rc;
+ return cxl_setup_regs(map);
}
/*
@@ -682,7 +614,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_warn(&pdev->dev,
"Device DVSEC not present, skip CXL.mem init\n");
- rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
if (rc)
return rc;
@@ -695,7 +627,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* still be useful for management functions so don't return an error.
*/
cxlds->component_reg_phys = CXL_RESOURCE_NONE;
- rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
--
2.34.1
From: Robert Richter <[email protected]>
When probing the Component Registers in function cxl_probe_regs()
there are also checks for the existence of the HDM and RAS
capabilities. The checks may fail for components that do not implement
the HDM capability causing the Component Registers setup to fail too.
Remove the checks for a generalized use of cxl_probe_regs() and check
them directly before mapping the RAS or HDM capabilities. This allows
it to setup other Component Registers esp. of an RCH Downstream Port,
which will be implemented in a follow-on patch.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/regs.c | 8 --------
drivers/cxl/pci.c | 2 ++
drivers/cxl/port.c | 5 ++++-
3 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index cb2a5b1c6db5..7e56ddf509c0 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -366,14 +366,6 @@ static int cxl_probe_regs(struct cxl_register_map *map)
case CXL_REGLOC_RBI_COMPONENT:
comp_map = &map->component_map;
cxl_probe_component_regs(map->dev, base, comp_map);
- if (!comp_map->hdm_decoder.valid) {
- dev_err(map->dev, "HDM decoder registers not found\n");
- return -ENXIO;
- }
-
- if (!comp_map->ras.valid)
- dev_dbg(map->dev, "RAS registers not found\n");
-
dev_dbg(map->dev, "Set up component registers\n");
break;
case CXL_REGLOC_RBI_MEMDEV:
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index ac17bc0430dc..945ca0304d68 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -630,6 +630,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
+ else if (!map.component_map.ras.valid)
+ dev_dbg(&pdev->dev, "RAS registers not found\n");
cxlds->component_reg_phys = map.resource;
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index c23b6164e1c0..e1c7efa9232e 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -102,8 +102,11 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
return rc;
cxlhdm = devm_cxl_setup_hdm(port, &info);
- if (IS_ERR(cxlhdm))
+ if (IS_ERR(cxlhdm)) {
+ if (PTR_ERR(cxlhdm) == -ENODEV)
+ dev_err(&port->dev, "HDM decoder registers not found\n");
return PTR_ERR(cxlhdm);
+ }
/* Cache the data early to ensure is_visible() works */
read_cdat_data(port);
--
2.34.1
From: Robert Richter <[email protected]>
Just moving code to reorder functions to later share cxl_get_chbs()
with add_host_bridge_uport().
This makes changes in the next patch visible. No other changes at all.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/acpi.c | 90 +++++++++++++++++++++++-----------------------
1 file changed, 45 insertions(+), 45 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 39227070da9b..4fd9fe32f830 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -327,51 +327,6 @@ __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
return NULL;
}
-/*
- * A host bridge is a dport to a CFMWS decode and it is a uport to the
- * dport (PCIe Root Ports) in the host bridge.
- */
-static int add_host_bridge_uport(struct device *match, void *arg)
-{
- struct cxl_port *root_port = arg;
- struct device *host = root_port->dev.parent;
- struct acpi_device *hb = to_cxl_host_bridge(host, match);
- struct acpi_pci_root *pci_root;
- struct cxl_dport *dport;
- struct cxl_port *port;
- struct device *bridge;
- int rc;
-
- if (!hb)
- return 0;
-
- pci_root = acpi_pci_find_root(hb->handle);
- bridge = pci_root->bus->bridge;
- dport = cxl_find_dport_by_dev(root_port, bridge);
- if (!dport) {
- dev_dbg(host, "host bridge expected and not found\n");
- return 0;
- }
-
- if (dport->rch) {
- dev_info(bridge, "host supports CXL (restricted)\n");
- return 0;
- }
-
- rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
- if (rc)
- return rc;
-
- port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
- dport);
- if (IS_ERR(port))
- return PTR_ERR(port);
-
- dev_info(bridge, "host supports CXL\n");
-
- return 0;
-}
-
struct cxl_chbs_context {
unsigned long long uid;
resource_size_t base;
@@ -464,6 +419,51 @@ static int add_host_bridge_dport(struct device *match, void *arg)
return 0;
}
+/*
+ * A host bridge is a dport to a CFMWS decode and it is a uport to the
+ * dport (PCIe Root Ports) in the host bridge.
+ */
+static int add_host_bridge_uport(struct device *match, void *arg)
+{
+ struct cxl_port *root_port = arg;
+ struct device *host = root_port->dev.parent;
+ struct acpi_device *hb = to_cxl_host_bridge(host, match);
+ struct acpi_pci_root *pci_root;
+ struct cxl_dport *dport;
+ struct cxl_port *port;
+ struct device *bridge;
+ int rc;
+
+ if (!hb)
+ return 0;
+
+ pci_root = acpi_pci_find_root(hb->handle);
+ bridge = pci_root->bus->bridge;
+ dport = cxl_find_dport_by_dev(root_port, bridge);
+ if (!dport) {
+ dev_dbg(host, "host bridge expected and not found\n");
+ return 0;
+ }
+
+ if (dport->rch) {
+ dev_info(bridge, "host supports CXL (restricted)\n");
+ return 0;
+ }
+
+ rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
+ if (rc)
+ return rc;
+
+ port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
+ dport);
+ if (IS_ERR(port))
+ return PTR_ERR(port);
+
+ dev_info(bridge, "host supports CXL\n");
+
+ return 0;
+}
+
static int add_root_nvdimm_bridge(struct device *match, void *data)
{
struct cxl_decoder *cxld;
--
2.34.1
From: Robert Richter <[email protected]>
CXL capabilities are stored in the Component Registers. To use them,
the specific I/O ranges of the capabilities must be determined by
probing the registers. For this, the whole Component Register range
needs to be mapped temporarily to detect the offset and length of a
capability range.
In order to use more than one capability of a component (e.g. RAS and
HDM) the Component Register are probed and its mappings created
multiple times. This also causes overlapping I/O ranges as the whole
Component Register range must be mapped again while a capability's I/O
range is already mapped.
Different capabilities cannot be setup at the same time. E.g. the RAS
capability must be made available as soon as the PCI driver is bound,
the HDM decoder is setup later during port enumeration. Moreover,
during early setup it is still unknown if a certain capability is
needed. A central capability setup is therefore not possible,
capabilities must be individually enabled once needed during
initialization.
To avoid a duplicate register probe and overlapping I/O mappings, only
probe the Component Registers one time and store the Component
Register mapping in struct port. The stored mappings can be used later
to iomap the capability register range when enabling the capability,
which will be implemented in a follow-on patch.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 26 ++++++++++++++++++++++++++
drivers/cxl/cxl.h | 2 ++
2 files changed, 28 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index eff91f141fde..34e929f1723b 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -686,6 +686,28 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
return ERR_PTR(rc);
}
+static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map,
+ resource_size_t component_reg_phys)
+{
+ if (component_reg_phys == CXL_RESOURCE_NONE)
+ return -ENODEV;
+
+ memset(map, 0, sizeof(*map));
+ map->dev = dev;
+ map->reg_type = CXL_REGLOC_RBI_COMPONENT;
+ map->resource = component_reg_phys;
+ map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
+
+ return cxl_setup_regs(map);
+}
+
+static inline int cxl_port_setup_regs(struct cxl_port *port,
+ resource_size_t component_reg_phys)
+{
+ return cxl_setup_comp_regs(&port->dev, &port->comp_map,
+ component_reg_phys);
+}
+
static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *uport,
resource_size_t component_reg_phys,
@@ -709,6 +731,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
if (rc)
goto err;
+ rc = cxl_port_setup_regs(port, component_reg_phys);
+ if (rc && rc != -ENODEV)
+ goto err;
+
rc = device_add(dev);
if (rc)
goto err;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index e5ae5f4e6669..c76e1f84ba61 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -552,6 +552,7 @@ struct cxl_dax_region {
* @regions: cxl_region_ref instances, regions mapped by this port
* @parent_dport: dport that points to this port in the parent
* @decoder_ida: allocator for decoder ids
+ * @comp_map: component register capability mappings
* @nr_dports: number of entries in @dports
* @hdm_end: track last allocated HDM decoder instance for allocation ordering
* @commit_end: cursor to track highest committed decoder for commit ordering
@@ -571,6 +572,7 @@ struct cxl_port {
struct xarray regions;
struct cxl_dport *parent_dport;
struct ida decoder_ida;
+ struct cxl_register_map comp_map;
int nr_dports;
int hdm_end;
int commit_end;
--
2.34.1
From: Robert Richter <[email protected]>
The RCRB is extracted already during ACPI CEDT table parsing while the
data of this is needed not earlier than dport creation. This
implementation comes with drawbacks: During ACPI table scan there is
already MMIO access including mapping and unmapping, but only ACPI
data should be collected here. The collected data must be transferred
through a couple of interfaces until it is finally consumed when
creating the dport. This causes complex data structures and function
interfaces. Additionally, RCRB parsing will be extended to also
extract AER data, it would be much easier do this at a later point
during port and dport creation when the data structures are available
to hold that data.
To simplify all that, probe the RCRB at a later point during RCH
downstream port creation. Change ACPI table parser to only extract the
base address of either the component registers or the RCRB. Parse and
extract the RCRB in devm_cxl_add_rch_dport().
This is in preparation to centralize all RCRB scanning.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/acpi.c | 52 ++++++++++++++++-------------------------
drivers/cxl/core/port.c | 21 +++++++++++++----
drivers/cxl/cxl.h | 1 -
3 files changed, 36 insertions(+), 38 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 7e1765b09e04..39227070da9b 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -373,20 +373,18 @@ static int add_host_bridge_uport(struct device *match, void *arg)
}
struct cxl_chbs_context {
- struct device *dev;
unsigned long long uid;
- resource_size_t rcrb;
- resource_size_t chbcr;
+ resource_size_t base;
u32 cxl_version;
};
-static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
+static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
const unsigned long end)
{
struct cxl_chbs_context *ctx = arg;
struct acpi_cedt_chbs *chbs;
- if (ctx->chbcr)
+ if (ctx->base)
return 0;
chbs = (struct acpi_cedt_chbs *) header;
@@ -395,23 +393,16 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
return 0;
ctx->cxl_version = chbs->cxl_version;
- ctx->rcrb = CXL_RESOURCE_NONE;
- ctx->chbcr = CXL_RESOURCE_NONE;
+ ctx->base = CXL_RESOURCE_NONE;
if (!chbs->base)
return 0;
- if (chbs->cxl_version != ACPI_CEDT_CHBS_VERSION_CXL11) {
- ctx->chbcr = chbs->base;
+ if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11 &&
+ chbs->length != CXL_RCRB_SIZE)
return 0;
- }
- if (chbs->length != CXL_RCRB_SIZE)
- return 0;
-
- ctx->rcrb = chbs->base;
- ctx->chbcr = cxl_rcrb_to_component(ctx->dev, chbs->base,
- CXL_RCRB_DOWNSTREAM);
+ ctx->base = chbs->base;
return 0;
}
@@ -440,36 +431,33 @@ static int add_host_bridge_dport(struct device *match, void *arg)
dev_dbg(match, "UID found: %lld\n", uid);
ctx = (struct cxl_chbs_context) {
- .dev = match,
.uid = uid,
};
- acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
+ acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
- if (!ctx.chbcr) {
+ if (!ctx.base) {
dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
uid);
return 0;
}
- if (ctx.rcrb != CXL_RESOURCE_NONE)
- dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.rcrb);
-
- if (ctx.chbcr == CXL_RESOURCE_NONE) {
- dev_warn(match, "CHBCR invalid for Host Bridge (UID %lld)\n",
+ if (ctx.base == CXL_RESOURCE_NONE) {
+ dev_warn(match, "CHBS invalid for Host Bridge (UID %lld)\n",
uid);
return 0;
}
- dev_dbg(match, "CHBCR found: %pa\n", &ctx.chbcr);
-
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
- if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11)
- dport = devm_cxl_add_rch_dport(root_port, bridge, uid,
- ctx.chbcr, ctx.rcrb);
- else
- dport = devm_cxl_add_dport(root_port, bridge, uid,
- ctx.chbcr);
+
+ if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
+ dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
+ dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
+ } else {
+ dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
+ dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
+ }
+
if (IS_ERR(dport))
return PTR_ERR(dport);
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index e7c284c890bc..1a3f8729a616 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -938,12 +938,25 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
if (!dport)
return ERR_PTR(-ENOMEM);
+ if (rcrb != CXL_RESOURCE_NONE) {
+ component_reg_phys = cxl_rcrb_to_component(dport_dev,
+ rcrb, CXL_RCRB_DOWNSTREAM);
+ if (component_reg_phys == CXL_RESOURCE_NONE) {
+ dev_warn(dport_dev, "Invalid Component Registers in RCRB");
+ return ERR_PTR(-ENXIO);
+ }
+
+ dport->rch = true;
+ }
+
+ if (component_reg_phys != CXL_RESOURCE_NONE)
+ dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
+ &component_reg_phys);
+
dport->dport = dport_dev;
dport->port_id = port_id;
dport->component_reg_phys = component_reg_phys;
dport->port = port;
- if (rcrb != CXL_RESOURCE_NONE)
- dport->rch = true;
dport->rcrb = rcrb;
cond_cxl_root_lock(port);
@@ -1004,14 +1017,12 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport, CXL);
* @port: the cxl_port that references this dport
* @dport_dev: firmware or PCI device representing the dport
* @port_id: identifier for this dport in a decoder's target list
- * @component_reg_phys: optional location of CXL component registers
* @rcrb: mandatory location of a Root Complex Register Block
*
* See CXL 3.0 9.11.8 CXL Devices Attached to an RCH
*/
struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
struct device *dport_dev, int port_id,
- resource_size_t component_reg_phys,
resource_size_t rcrb)
{
struct cxl_dport *dport;
@@ -1022,7 +1033,7 @@ struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
}
dport = __devm_cxl_add_dport(port, dport_dev, port_id,
- component_reg_phys, rcrb);
+ CXL_RESOURCE_NONE, rcrb);
if (IS_ERR(dport)) {
dev_dbg(dport_dev, "failed to add RCH dport to %s: %ld\n",
dev_name(&port->dev), PTR_ERR(dport));
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index f93a28538962..a5cd661face2 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -671,7 +671,6 @@ struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port,
resource_size_t component_reg_phys);
struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
struct device *dport_dev, int port_id,
- resource_size_t component_reg_phys,
resource_size_t rcrb);
struct cxl_decoder *to_cxl_decoder(struct device *dev);
--
2.34.1
From: Robert Richter <[email protected]>
Reading code like dport->dport does not immediately suggest that this
points to the corresponding device structure of the dport. Rename
struct member @dport to @dev.
While at it, also rename @new argument of add_dport() to @dport. This
better describes the variable as a dport (e.g. new->dport becomes to
dport->dev).
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 20 ++++++++++----------
drivers/cxl/core/region.c | 4 ++--
drivers/cxl/cxl.h | 4 ++--
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 618865ca6a9f..66f567480238 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -605,7 +605,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
if (!parent_dport)
return 0;
- rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport->kobj,
+ rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dev->kobj,
"parent_dport");
if (rc)
return rc;
@@ -658,7 +658,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
if (iter->host_bridge)
port->host_bridge = iter->host_bridge;
else if (parent_dport->rch)
- port->host_bridge = parent_dport->dport;
+ port->host_bridge = parent_dport->dev;
else
port->host_bridge = iter->uport;
dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
@@ -847,22 +847,22 @@ static struct cxl_dport *find_dport(struct cxl_port *port, int id)
return NULL;
}
-static int add_dport(struct cxl_port *port, struct cxl_dport *new)
+static int add_dport(struct cxl_port *port, struct cxl_dport *dport)
{
struct cxl_dport *dup;
int rc;
device_lock_assert(&port->dev);
- dup = find_dport(port, new->port_id);
+ dup = find_dport(port, dport->port_id);
if (dup) {
dev_err(&port->dev,
"unable to add dport%d-%s non-unique port id (%s)\n",
- new->port_id, dev_name(new->dport),
- dev_name(dup->dport));
+ dport->port_id, dev_name(dport->dev),
+ dev_name(dup->dev));
return -EBUSY;
}
- rc = xa_insert(&port->dports, (unsigned long)new->dport, new,
+ rc = xa_insert(&port->dports, (unsigned long)dport->dev, dport,
GFP_KERNEL);
if (rc)
return rc;
@@ -895,8 +895,8 @@ static void cxl_dport_remove(void *data)
struct cxl_dport *dport = data;
struct cxl_port *port = dport->port;
- xa_erase(&port->dports, (unsigned long) dport->dport);
- put_device(dport->dport);
+ xa_erase(&port->dports, (unsigned long) dport->dev);
+ put_device(dport->dev);
}
static void cxl_dport_unlink(void *data)
@@ -954,7 +954,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
&component_reg_phys);
- dport->dport = dport_dev;
+ dport->dev = dport_dev;
dport->port_id = port_id;
dport->component_reg_phys = component_reg_phys;
dport->port = port;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index f822de44bee0..8886c6201998 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1162,7 +1162,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
dev_name(port->uport), dev_name(&port->dev),
dev_name(&cxlsd->cxld.dev),
- dev_name(ep->dport->dport),
+ dev_name(ep->dport->dev),
cxl_rr->nr_targets_set);
return -ENXIO;
}
@@ -1173,7 +1173,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
cxl_rr->nr_targets_set += inc;
dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
dev_name(port->uport), dev_name(&port->dev),
- cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport),
+ cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
return 0;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 29e0bd2b8f2a..a8bda2c74a85 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -594,7 +594,7 @@ struct cxl_rcrb_info {
/**
* struct cxl_dport - CXL downstream port
- * @dport: PCI bridge or firmware device representing the downstream link
+ * @dev: PCI bridge or firmware device representing the downstream link
* @port: reference to cxl_port that contains this downstream port
* @port_id: unique hardware identifier for dport in decoder target list
* @component_reg_phys: downstream port component registers
@@ -602,7 +602,7 @@ struct cxl_rcrb_info {
* @rcrb: Data about the Root Complex Register Block layout
*/
struct cxl_dport {
- struct device *dport;
+ struct device *dev;
struct cxl_port *port;
int port_id;
resource_size_t component_reg_phys;
--
2.34.1
From: Robert Richter <[email protected]>
The corresponding device of a register mapping is used for devm
operations and logging. For operations with struct cxl_register_map
the device needs to be kept track separately. To simpify the involved
function interfaces, add @dev to cxl_register_map.
While at it also reorder function arguments of cxl_map_device_regs()
and cxl_map_component_regs() to have the object @cxl_register_map
first.
In a result a bunch of functions are available to be used with a
@cxl_register_map object.
This patch is in preparation of reworking the component register setup
code.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/hdm.c | 4 ++--
drivers/cxl/core/regs.c | 22 ++++++++++++---------
drivers/cxl/cxl.h | 10 ++++++----
drivers/cxl/pci.c | 42 ++++++++++++++++++++---------------------
4 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 7889ff203a34..5abfa9276dac 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
struct cxl_component_regs *regs)
{
struct cxl_register_map map = {
+ .dev = &port->dev,
.resource = port->component_reg_phys,
.base = crb,
.max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
@@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
return -ENODEV;
}
- return cxl_map_component_regs(&port->dev, regs, &map,
- BIT(CXL_CM_CAP_CAP_ID_HDM));
+ return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
}
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 08da4c917f99..9888bdf43e55 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -199,8 +199,9 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
return ret_val;
}
-int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
- struct cxl_register_map *map, unsigned long map_mask)
+int cxl_map_component_regs(struct cxl_register_map *map,
+ struct cxl_component_regs *regs,
+ unsigned long map_mask)
{
struct mapinfo {
struct cxl_reg_map *rmap;
@@ -213,16 +214,16 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
for (i = 0; i < ARRAY_SIZE(mapinfo); i++) {
struct mapinfo *mi = &mapinfo[i];
- resource_size_t phys_addr;
+ resource_size_t addr;
resource_size_t length;
if (!mi->rmap->valid)
continue;
if (!test_bit(mi->rmap->id, &map_mask))
continue;
- phys_addr = map->resource + mi->rmap->offset;
+ addr = map->resource + mi->rmap->offset;
length = mi->rmap->size;
- *(mi->addr) = devm_cxl_iomap_block(dev, phys_addr, length);
+ *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
if (!*(mi->addr))
return -ENOMEM;
}
@@ -231,9 +232,8 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
}
EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
-int cxl_map_device_regs(struct device *dev,
- struct cxl_device_regs *regs,
- struct cxl_register_map *map)
+int cxl_map_device_regs(struct cxl_register_map *map,
+ struct cxl_device_regs *regs)
{
resource_size_t phys_addr = map->resource;
struct mapinfo {
@@ -256,7 +256,7 @@ int cxl_map_device_regs(struct device *dev,
addr = phys_addr + mi->rmap->offset;
length = mi->rmap->size;
- *(mi->addr) = devm_cxl_iomap_block(dev, addr, length);
+ *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
if (!*(mi->addr))
return -ENOMEM;
}
@@ -302,7 +302,10 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
u32 regloc_size, regblocks;
int regloc, i;
+ memset(map, 0, sizeof(*map));
+ map->dev = &pdev->dev;
map->resource = CXL_RESOURCE_NONE;
+
regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
CXL_DVSEC_REG_LOCATOR);
if (!regloc)
@@ -328,6 +331,7 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
}
map->resource = CXL_RESOURCE_NONE;
+
return -ENODEV;
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index a8bda2c74a85..095b767c21e9 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -231,6 +231,7 @@ struct cxl_device_reg_map {
/**
* struct cxl_register_map - DVSEC harvested register block mapping parameters
+ * @dev: device for devm operations and logging
* @base: virtual base of the register-block-BAR + @block_offset
* @resource: physical resource base of the register block
* @max_size: maximum mapping size to perform register search
@@ -239,6 +240,7 @@ struct cxl_device_reg_map {
* @device_map: cxl_reg_maps for device registers
*/
struct cxl_register_map {
+ struct device *dev;
void __iomem *base;
resource_size_t resource;
resource_size_t max_size;
@@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
struct cxl_device_reg_map *map);
-int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
- struct cxl_register_map *map,
+int cxl_map_component_regs(struct cxl_register_map *map,
+ struct cxl_component_regs *regs,
unsigned long map_mask);
-int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
- struct cxl_register_map *map);
+int cxl_map_device_regs(struct cxl_register_map *map,
+ struct cxl_device_regs *regs);
enum cxl_regloc_type;
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 0872f2233ed0..2a9f65be148b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,61 +274,59 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
-static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
+static int cxl_map_regblock(struct cxl_register_map *map)
{
- struct device *dev = &pdev->dev;
-
map->base = ioremap(map->resource, map->max_size);
if (!map->base) {
- dev_err(dev, "failed to map registers\n");
+ dev_err(map->dev, "failed to map registers\n");
return -ENOMEM;
}
- dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
+ dev_dbg(map->dev, "Mapped CXL Memory Device resource %pa\n",
+ &map->resource);
+
return 0;
}
-static void cxl_unmap_regblock(struct pci_dev *pdev,
- struct cxl_register_map *map)
+static void cxl_unmap_regblock(struct cxl_register_map *map)
{
iounmap(map->base);
map->base = NULL;
}
-static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
+static int cxl_probe_regs(struct cxl_register_map *map)
{
struct cxl_component_reg_map *comp_map;
struct cxl_device_reg_map *dev_map;
- struct device *dev = &pdev->dev;
void __iomem *base = map->base;
switch (map->reg_type) {
case CXL_REGLOC_RBI_COMPONENT:
comp_map = &map->component_map;
- cxl_probe_component_regs(dev, base, comp_map);
+ cxl_probe_component_regs(map->dev, base, comp_map);
if (!comp_map->hdm_decoder.valid) {
- dev_err(dev, "HDM decoder registers not found\n");
+ dev_err(map->dev, "HDM decoder registers not found\n");
return -ENXIO;
}
if (!comp_map->ras.valid)
- dev_dbg(dev, "RAS registers not found\n");
+ dev_dbg(map->dev, "RAS registers not found\n");
- dev_dbg(dev, "Set up component registers\n");
+ dev_dbg(map->dev, "Set up component registers\n");
break;
case CXL_REGLOC_RBI_MEMDEV:
dev_map = &map->device_map;
- cxl_probe_device_regs(dev, base, dev_map);
+ cxl_probe_device_regs(map->dev, base, dev_map);
if (!dev_map->status.valid || !dev_map->mbox.valid ||
!dev_map->memdev.valid) {
- dev_err(dev, "registers not found: %s%s%s\n",
+ dev_err(map->dev, "registers not found: %s%s%s\n",
!dev_map->status.valid ? "status " : "",
!dev_map->mbox.valid ? "mbox " : "",
!dev_map->memdev.valid ? "memdev " : "");
return -ENXIO;
}
- dev_dbg(dev, "Probing device registers...\n");
+ dev_dbg(map->dev, "Probing device registers...\n");
break;
default:
break;
@@ -346,12 +344,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
if (rc)
return rc;
- rc = cxl_map_regblock(pdev, map);
+ rc = cxl_map_regblock(map);
if (rc)
return rc;
- rc = cxl_probe_regs(pdev, map);
- cxl_unmap_regblock(pdev, map);
+ rc = cxl_probe_regs(map);
+ cxl_unmap_regblock(map);
return rc;
}
@@ -688,7 +686,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
- rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
+ rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
if (rc)
return rc;
@@ -703,8 +701,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
cxlds->component_reg_phys = map.resource;
- rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
- &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
+ rc = cxl_map_component_regs(&map, &cxlds->regs.component,
+ BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
--
2.34.1
From: Robert Richter <[email protected]>
CXL RAS capabilities must be enabled and accessible as soon as the CXL
endpoint is detected in the PCI hierarchy and bound to the cxl_pci
driver. This needs to be independent of other modules such as cxl_port
or cxl_mem.
CXL RAS capabilities reside in the Component Registers. For an RCH
this is determined by probing RCRB which is implemented very late once
the CXL Memory Device is created.
Change this by moving the RCRB probe to the cxl_pci driver. Do this by
using a new introduced function cxl_pci_find_port() similar to
cxl_mem_find_port() to determine the involved dport by the endpoint's
PCI handle. Plug this into the existing cxl_pci_setup_regs() function
to setup Component Registers. Probe the RCRB in case the Component
Registers cannot be located through the CXL Register Locator
capability.
This unifies code and early sets up the Component Registers at the
same time for both, VH and RCH mode. Only the cxl_pci driver is
involved for this. This allows an early mapping of the CXL RAS
capability registers.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 7 +++++++
drivers/cxl/cxl.h | 2 ++
drivers/cxl/mem.c | 12 ------------
drivers/cxl/pci.c | 37 ++++++++++++++++++++++++++++++++++++-
4 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 66f567480238..eff91f141fde 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1477,6 +1477,13 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, CXL);
+struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
+ struct cxl_dport **dport)
+{
+ return find_cxl_port(pdev->dev.parent, dport);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, CXL);
+
struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
struct cxl_dport **dport)
{
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 1c6fe53e9dc7..e5ae5f4e6669 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -670,6 +670,8 @@ struct cxl_port *find_cxl_root(struct cxl_port *port);
int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
void cxl_bus_rescan(void);
void cxl_bus_drain(void);
+struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
+ struct cxl_dport **dport);
struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
struct cxl_dport **dport);
bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd);
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 7ecdaa7f9315..0643852444f3 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -65,18 +65,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
ep->next = down;
}
- /*
- * The component registers for an RCD might come from the
- * host-bridge RCRB if they are not already mapped via the
- * typical register locator mechanism.
- */
- if (parent_dport->rch &&
- cxlds->component_reg_phys == CXL_RESOURCE_NONE) {
- cxlds->component_reg_phys =
- cxl_probe_rcrb(&cxlmd->dev, parent_dport->rcrb.base,
- NULL, CXL_RCRB_UPSTREAM);
- }
-
endpoint = devm_cxl_add_port(host, &cxlmd->dev,
cxlds->component_reg_phys,
parent_dport);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 945ca0304d68..54c486cd65dd 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,13 +274,48 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
+/* Extract RCRB, use same function interface as cxl_find_regblock(). */
+static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
+ enum cxl_regloc_type type,
+ struct cxl_register_map *map)
+{
+ struct cxl_dport *dport;
+ resource_size_t component_reg_phys;
+
+ memset(map, 0, sizeof(*map));
+ map->dev = &pdev->dev;
+ map->resource = CXL_RESOURCE_NONE;
+
+ if (type != CXL_REGLOC_RBI_COMPONENT)
+ return -ENODEV;
+
+ if (!cxl_pci_find_port(pdev, &dport) || !dport->rch)
+ return -ENXIO;
+
+ component_reg_phys = cxl_probe_rcrb(&pdev->dev, dport->rcrb.base,
+ NULL, CXL_RCRB_UPSTREAM);
+ if (component_reg_phys == CXL_RESOURCE_NONE)
+ return -ENXIO;
+
+ map->resource = component_reg_phys;
+ map->reg_type = type;
+ map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
+
+ return 0;
+}
+
static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
int rc;
+ /*
+ * If the Register Locator DVSEC does not contain the
+ * Component Registers, try to extract them from the RCRB if
+ * it is an RCH.
+ */
rc = cxl_find_regblock(pdev, type, map);
- if (rc)
+ if (rc && cxl_rcrb_get_comp_regs(pdev, type, map))
return rc;
return cxl_setup_regs(map);
--
2.34.1
From: Dan Williams <[email protected]>
Prepare cxl_probe_rcrb() for retrieving more than just the component
register block. The RCH AER handling code wants to get back to the AER
capability that happens to be MMIO mapped rather then configuration
cycles.
Move RCRB specific downstream port data, like the RCRB base and the
AER capability offset, into its own data structure ('struct
cxl_rcrb_info') for cxl_probe_rcrb() to fill. Extend 'struct
cxl_dport' to include a 'struct cxl_rcrb_info' attribute.
This centralizes all RCRB scanning in one routine.
Signed-off-by: Dan Williams <[email protected]>
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 7 ++++---
drivers/cxl/core/regs.c | 10 ++++++----
drivers/cxl/cxl.h | 19 ++++++++++++-------
drivers/cxl/mem.c | 16 +++++++++-------
tools/testing/cxl/Kbuild | 2 +-
tools/testing/cxl/test/cxl.c | 10 ++++++----
tools/testing/cxl/test/mock.c | 12 ++++++------
tools/testing/cxl/test/mock.h | 7 ++++---
8 files changed, 48 insertions(+), 35 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 1a3f8729a616..618865ca6a9f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -939,8 +939,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
return ERR_PTR(-ENOMEM);
if (rcrb != CXL_RESOURCE_NONE) {
- component_reg_phys = cxl_rcrb_to_component(dport_dev,
- rcrb, CXL_RCRB_DOWNSTREAM);
+ component_reg_phys =
+ cxl_probe_rcrb(dport_dev, rcrb, &dport->rcrb,
+ CXL_RCRB_DOWNSTREAM);
if (component_reg_phys == CXL_RESOURCE_NONE) {
dev_warn(dport_dev, "Invalid Component Registers in RCRB");
return ERR_PTR(-ENXIO);
@@ -957,7 +958,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->port_id = port_id;
dport->component_reg_phys = component_reg_phys;
dport->port = port;
- dport->rcrb = rcrb;
+ dport->rcrb.base = rcrb;
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 1476a0299c9b..08da4c917f99 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -332,9 +332,8 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
-resource_size_t cxl_rcrb_to_component(struct device *dev,
- resource_size_t rcrb,
- enum cxl_rcrb which)
+resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
+ struct cxl_rcrb_info *ri, enum cxl_rcrb which)
{
resource_size_t component_reg_phys;
void __iomem *addr;
@@ -344,6 +343,8 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
if (which == CXL_RCRB_UPSTREAM)
rcrb += SZ_4K;
+ else if (ri)
+ ri->base = rcrb;
/*
* RCRB's BAR[0..1] point to component block containing CXL
@@ -364,6 +365,7 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
cmd = readw(addr + PCI_COMMAND);
bar0 = readl(addr + PCI_BASE_ADDRESS_0);
bar1 = readl(addr + PCI_BASE_ADDRESS_1);
+
iounmap(addr);
release_mem_region(rcrb, SZ_4K);
@@ -395,4 +397,4 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
return component_reg_phys;
}
-EXPORT_SYMBOL_NS_GPL(cxl_rcrb_to_component, CXL);
+EXPORT_SYMBOL_NS_GPL(cxl_probe_rcrb, CXL);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index a5cd661face2..29e0bd2b8f2a 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -267,9 +267,9 @@ enum cxl_rcrb {
CXL_RCRB_DOWNSTREAM,
CXL_RCRB_UPSTREAM,
};
-resource_size_t cxl_rcrb_to_component(struct device *dev,
- resource_size_t rcrb,
- enum cxl_rcrb which);
+struct cxl_rcrb_info;
+resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
+ struct cxl_rcrb_info *ri, enum cxl_rcrb which);
#define CXL_RESOURCE_NONE ((resource_size_t) -1)
#define CXL_TARGET_STRLEN 20
@@ -587,22 +587,27 @@ cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
return xa_load(&port->dports, (unsigned long)dport_dev);
}
+struct cxl_rcrb_info {
+ resource_size_t base;
+ u16 aer_cap;
+};
+
/**
* struct cxl_dport - CXL downstream port
* @dport: PCI bridge or firmware device representing the downstream link
+ * @port: reference to cxl_port that contains this downstream port
* @port_id: unique hardware identifier for dport in decoder target list
* @component_reg_phys: downstream port component registers
- * @rcrb: base address for the Root Complex Register Block
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
- * @port: reference to cxl_port that contains this downstream port
+ * @rcrb: Data about the Root Complex Register Block layout
*/
struct cxl_dport {
struct device *dport;
+ struct cxl_port *port;
int port_id;
resource_size_t component_reg_phys;
- resource_size_t rcrb;
bool rch;
- struct cxl_port *port;
+ struct cxl_rcrb_info rcrb;
};
/**
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 519edd0eb196..7ecdaa7f9315 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
struct cxl_port *parent_port = parent_dport->port;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *endpoint, *iter, *down;
- resource_size_t component_reg_phys;
int rc;
/*
@@ -71,12 +70,15 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
* host-bridge RCRB if they are not already mapped via the
* typical register locator mechanism.
*/
- if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
- component_reg_phys = cxl_rcrb_to_component(
- &cxlmd->dev, parent_dport->rcrb, CXL_RCRB_UPSTREAM);
- else
- component_reg_phys = cxlds->component_reg_phys;
- endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
+ if (parent_dport->rch &&
+ cxlds->component_reg_phys == CXL_RESOURCE_NONE) {
+ cxlds->component_reg_phys =
+ cxl_probe_rcrb(&cxlmd->dev, parent_dport->rcrb.base,
+ NULL, CXL_RCRB_UPSTREAM);
+ }
+
+ endpoint = devm_cxl_add_port(host, &cxlmd->dev,
+ cxlds->component_reg_phys,
parent_dport);
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 6f9347ade82c..32b9d398d399 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -12,7 +12,7 @@ ldflags-y += --wrap=devm_cxl_enumerate_decoders
ldflags-y += --wrap=cxl_await_media_ready
ldflags-y += --wrap=cxl_hdm_decode_init
ldflags-y += --wrap=cxl_dvsec_rr_decode
-ldflags-y += --wrap=cxl_rcrb_to_component
+ldflags-y += --wrap=cxl_probe_rcrb
DRIVERS := ../../../drivers
CXL_SRC := $(DRIVERS)/cxl
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index bf00dc52fe96..790dec597a70 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -971,12 +971,14 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
return 0;
}
-resource_size_t mock_cxl_rcrb_to_component(struct device *dev,
- resource_size_t rcrb,
- enum cxl_rcrb which)
+resource_size_t mock_cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
+ struct cxl_rcrb_info *ri, enum cxl_rcrb which)
{
dev_dbg(dev, "rcrb: %pa which: %d\n", &rcrb, which);
+ if (which == CXL_RCRB_DOWNSTREAM)
+ ri->base = rcrb;
+
return (resource_size_t) which + 1;
}
@@ -988,7 +990,7 @@ static struct cxl_mock_ops cxl_mock_ops = {
.is_mock_dev = is_mock_dev,
.acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
.acpi_evaluate_integer = mock_acpi_evaluate_integer,
- .cxl_rcrb_to_component = mock_cxl_rcrb_to_component,
+ .cxl_probe_rcrb = mock_cxl_probe_rcrb,
.acpi_pci_find_root = mock_acpi_pci_find_root,
.devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports,
.devm_cxl_setup_hdm = mock_cxl_setup_hdm,
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index 284416527644..4790055fe25a 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -259,9 +259,9 @@ int __wrap_cxl_dvsec_rr_decode(struct device *dev, int dvsec,
}
EXPORT_SYMBOL_NS_GPL(__wrap_cxl_dvsec_rr_decode, CXL);
-resource_size_t __wrap_cxl_rcrb_to_component(struct device *dev,
- resource_size_t rcrb,
- enum cxl_rcrb which)
+resource_size_t __wrap_cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
+ struct cxl_rcrb_info *ri,
+ enum cxl_rcrb which)
{
int index;
resource_size_t component_reg_phys;
@@ -269,14 +269,14 @@ resource_size_t __wrap_cxl_rcrb_to_component(struct device *dev,
if (ops && ops->is_mock_port(dev))
component_reg_phys =
- ops->cxl_rcrb_to_component(dev, rcrb, which);
+ ops->cxl_probe_rcrb(dev, rcrb, ri, which);
else
- component_reg_phys = cxl_rcrb_to_component(dev, rcrb, which);
+ component_reg_phys = cxl_probe_rcrb(dev, rcrb, ri, which);
put_cxl_mock_ops(index);
return component_reg_phys;
}
-EXPORT_SYMBOL_NS_GPL(__wrap_cxl_rcrb_to_component, CXL);
+EXPORT_SYMBOL_NS_GPL(__wrap_cxl_probe_rcrb, CXL);
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(ACPI);
diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
index bef8817b01f2..7ef21356d052 100644
--- a/tools/testing/cxl/test/mock.h
+++ b/tools/testing/cxl/test/mock.h
@@ -15,9 +15,10 @@ struct cxl_mock_ops {
acpi_string pathname,
struct acpi_object_list *arguments,
unsigned long long *data);
- resource_size_t (*cxl_rcrb_to_component)(struct device *dev,
- resource_size_t rcrb,
- enum cxl_rcrb which);
+ resource_size_t (*cxl_probe_rcrb)(struct device *dev,
+ resource_size_t rcrb,
+ struct cxl_rcrb_info *ri,
+ enum cxl_rcrb which);
struct acpi_pci_root *(*acpi_pci_find_root)(acpi_handle handle);
bool (*is_mock_bus)(struct pci_bus *bus);
bool (*is_mock_port)(struct device *dev);
--
2.34.1
The restricted CXL host (RCH) error handler will log protocol errors
using AER and RAS status registers. The AER and RAS registers need
to be virtually memory mapped before enabling interrupts. Update
__devm_cxl_add_dport() to include RCH RAS and AER mapping.
Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
the RCH downstream port's mapped registers.
The RCH contains root command AER registers that should not be
enabled.[1] Disable these to prevent root port interrupt generation.
[1] CXL3.0 - 12.2.1.1 RCH Downstream Port-detected Errors
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 64 +++++++++++++++++++++++++++++++++++++++++
drivers/cxl/core/regs.c | 1 +
drivers/cxl/cxl.h | 11 +++++++
3 files changed, 76 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index d147f08780d0..80c643254b86 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <linux/aer.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <cxl.h>
@@ -940,6 +941,63 @@ static void cxl_dport_unlink(void *data)
sysfs_remove_link(&port->dev.kobj, link_name);
}
+static void cxl_disable_rch_root_ints(struct cxl_dport *dport)
+{
+ void __iomem *aer_base = dport->regs.dport_aer;
+ u32 aer_cmd_mask, aer_cmd;
+
+ if (!dport->rch || !aer_base)
+ return;
+
+ /*
+ * Disable RCH root port command interrupts.
+ * CXL3.0 12.2.1.1 - RCH Downstream Port-detected Errors
+ *
+ * This sequnce may not be necessary. CXL spec states disabling
+ * the root cmd register's interrupts is required. But, PCI spec
+ * shows these are disabled by default on reset.
+ */
+ aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
+ PCI_ERR_ROOT_CMD_NONFATAL_EN |
+ PCI_ERR_ROOT_CMD_FATAL_EN);
+ aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
+ aer_cmd &= ~aer_cmd_mask;
+ writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
+}
+
+static int cxl_dport_map_rch_aer(struct cxl_dport *dport)
+{
+ struct cxl_rcrb_info *ri = &dport->rcrb;
+ resource_size_t aer_phys;
+ void __iomem *dport_aer;
+
+ if (!dport->rch || !ri->aer_cap)
+ return -ENODEV;
+
+ aer_phys = ri->aer_cap + ri->base;
+ dport_aer = devm_cxl_iomap_block(dport->dev, aer_phys,
+ sizeof(struct aer_capability_regs));
+ if (!dport_aer)
+ return -ENOMEM;
+
+ dport->regs.dport_aer = dport_aer;
+
+ return 0;
+}
+
+static int cxl_dport_map_regs(struct cxl_dport *dport)
+{
+ struct cxl_register_map *map = &dport->comp_map;
+
+ if (!map->component_map.ras.valid)
+ dev_dbg(map->dev, "RAS registers not found\n");
+ else if (cxl_map_component_regs(map, &dport->regs.component,
+ BIT(CXL_CM_CAP_CAP_ID_RAS)))
+ dev_dbg(dport->dev, "Failed to map RAS capability.\n");
+
+ return cxl_dport_map_rch_aer(dport);
+}
+
static struct cxl_dport *
__devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
int port_id, resource_size_t component_reg_phys,
@@ -994,6 +1052,12 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
if (rc && rc != -ENODEV)
return ERR_PTR(rc);
+ rc = cxl_dport_map_regs(dport);
+ if (rc && rc != -ENODEV)
+ return ERR_PTR(rc);
+
+ cxl_disable_rch_root_ints(dport);
+
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
cond_cxl_root_unlock(port);
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 045abc11add8..b34f9e04cae4 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -198,6 +198,7 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
return ret_val;
}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);
int cxl_map_component_regs(struct cxl_register_map *map,
struct cxl_component_regs *regs,
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 6134644b51f8..0e0bcbefefaf 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -209,6 +209,13 @@ struct cxl_regs {
struct_group_tagged(cxl_device_regs, device_regs,
void __iomem *status, *mbox, *memdev;
);
+ /*
+ * RCH downstream port specific RAS register
+ * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
+ */
+ struct_group_tagged(cxl_rch_regs, rch_regs,
+ void __iomem *dport_aer;
+ );
};
struct cxl_reg_map {
@@ -255,6 +262,8 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
struct cxl_device_reg_map *map);
+void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
+ resource_size_t length);
int cxl_map_component_regs(struct cxl_register_map *map,
struct cxl_component_regs *regs,
unsigned long map_mask);
@@ -603,6 +612,7 @@ struct cxl_rcrb_info {
* @port_id: unique hardware identifier for dport in decoder target list
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
* @rcrb: Data about the Root Complex Register Block layout
+ * @regs: Dport parsed register blocks
*/
struct cxl_dport {
struct device *dev;
@@ -611,6 +621,7 @@ struct cxl_dport {
int port_id;
bool rch;
struct cxl_rcrb_info rcrb;
+ struct cxl_regs regs;
};
/**
--
2.34.1
From: Robert Richter <[email protected]>
Same as for ports, also store the downstream port's Component Register
mappings, use struct cxl_dport for that.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 11 +++++++++++
drivers/cxl/cxl.h | 2 ++
2 files changed, 13 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 34e929f1723b..db2ba0c886e2 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -708,6 +708,13 @@ static inline int cxl_port_setup_regs(struct cxl_port *port,
component_reg_phys);
}
+static inline int cxl_dport_setup_regs(struct cxl_dport *dport,
+ resource_size_t component_reg_phys)
+{
+ return cxl_setup_comp_regs(dport->dev, &dport->comp_map,
+ component_reg_phys);
+}
+
static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *uport,
resource_size_t component_reg_phys,
@@ -986,6 +993,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->port = port;
dport->rcrb.base = rcrb;
+ rc = cxl_dport_setup_regs(dport, component_reg_phys);
+ if (rc && rc != -ENODEV)
+ return ERR_PTR(rc);
+
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
cond_cxl_root_unlock(port);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index c76e1f84ba61..dc83c1d0396e 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -601,6 +601,7 @@ struct cxl_rcrb_info {
* struct cxl_dport - CXL downstream port
* @dev: PCI bridge or firmware device representing the downstream link
* @port: reference to cxl_port that contains this downstream port
+ * @comp_map: component register capability mappings
* @port_id: unique hardware identifier for dport in decoder target list
* @component_reg_phys: downstream port component registers
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
@@ -609,6 +610,7 @@ struct cxl_rcrb_info {
struct cxl_dport {
struct device *dev;
struct cxl_port *port;
+ struct cxl_register_map comp_map;
int port_id;
resource_size_t component_reg_phys;
bool rch;
--
2.34.1
Dropped the change log somehow, Adding here.
Changes in V4:
- Made port RAS register discovery common and called from
__devm_cxl_add_dport().
- Changed RCH AER register discovery to be called from
__devm_cxl_add_dport().
- Changed RAS and RCH AER register mapping to be called from
__devm_cxl_add_dport().
- Changed component register mapping to use common mapping,
cxl_map_component_regs().
- Added cxl_regs to 'struct cxl_dport' for providing RCH downstream port
mapped registers used in error handler.
- Refactored cxl_map_reg() to be like devm_cxl_iomap_block().
- PCI/AER:
- Removed patch for cper_mem_err_unpack().
- Renamed cper_print_aer() to pci_print_aer().
- Changed pci_print_aer() export to use
EXPORT_SYMBOL_NS_GPL(pci_print_aer, CXL).
- Improved description of PCIEAER_CXL option in Kconfig.
- Renamed function to pci_aer_unmask_internal_errors(), added
pcie_aer_is_native() check.
- Improved comments and added spec refs.
- Renamed functions to cxl_rch_handle_error*().
- Modified cxl_rch_handle_error_iter() to only call the handler
callbacks, this also simplifies refcounting of the pdev.
- Refactored handle_error_source(), created pci_aer_handle_error().
- Changed printk messages to pci_*() variants.
- Added check for pcie_aer_is_native() to the RCEC.
- Introduced function cxl_rch_enable_rcec().
- Updated patch description ("PCI/AER: Forward RCH downstream
port-detected errors to the CXL.mem dev handler").
Changes in V3:
- Correct base commit in cover sheet.
- Change hardcoded return 0 to NULL in regs.c.
- Remove calls to pci_disable_pcie_error_reporting(pdev) and
pci_enable_pcie_error_reporting(pdev) in mem.c;
- Move RCEC interrupt unmask to PCIe port AER driver's probe.
- Fixes missing PCIEAER and PCIEPORTBUS config option error.
- Rename cxl_rcrb_setup() to cxl_setup_rcrb() in mem.c.
- Update cper_mem_err_unpack() patch subject and description.
Changes in V2:
- Refactor RCH initialization into cxl_mem driver.
- Includes RCH RAS and AER register discovery and mapping.
- Add RCEC protocol error interrupt forwarding to CXL endpoint
handler.
- Change AER and RAS logging to use existing trace routines.
- Enable RCEC AER internal errors.
Regards,
Terry
On 5/23/23 18:21, Terry Bowman wrote:
> Patches #1 to #16 are a rework of the Component Register setup. This
> is needed to share multiple CXL capabilities (HDM and RAS) for the
> same component, also there can be different components implementing
> the same capability, finally RCH mode should be supported too. The
> general approach to solve this is to:
>
> * Unify code for components and capabilities in VH and RCH modes.
>
> * Early setup of the Component Register base address.
>
> * Create and store the register mappings to later use it for mapping
> the capability I/O ranges.
>
> Patches #17 to #23 enable CXL RCH error handling. These are needed because
> RCH downstream port protocol error handling is implemented uniquely and not
> currently supported. These patches address the following:
>
> * Discovery and mapping of RCH downstream port AER registers.
>
> * AER portdrv changes to support CXL RCH protocol errors.
>
> * Interrupt setup specific to RCH mode: enabling RCEC internal
> errors and disabling root port interrupts.
>
> Dan Williams (1):
> cxl/rch: Prepare for caching the MMIO mapped PCIe AER capability
>
> Robert Richter (16):
> cxl/acpi: Probe RCRB later during RCH downstream port creation
> cxl: Rename member @dport of struct cxl_dport to @dev
> cxl/core/regs: Add @dev to cxl_register_map
> cxl/acpi: Moving add_host_bridge_uport() around
> cxl/acpi: Directly bind the CEDT detected CHBCR to the Host Bridge's
> port
> cxl/regs: Remove early capability checks in Component Register setup
> cxl/pci: Early setup RCH dport component registers from RCRB
> cxl/port: Store the port's Component Register mappings in struct
> cxl_port
> cxl/port: Store the downstream port's Component Register mappings in
> struct cxl_dport
> cxl/pci: Store the endpoint's Component Register mappings in struct
> cxl_dev_state
> cxl/hdm: Use stored Component Register mappings to map HDM decoder
> capability
> cxl/port: Remove Component Register base address from struct cxl_port
> cxl/port: Remove Component Register base address from struct cxl_dport
> cxl/pci: Remove Component Register base address from struct
> cxl_dev_state
> PCI/AER: Forward RCH downstream port-detected errors to the CXL.mem
> dev handler
> PCI/AER: Unmask RCEC internal errors to enable RCH downstream port
> error handling
>
> Terry Bowman (6):
> cxl/pci: Refactor component register discovery for reuse
> cxl/pci: Add RCH downstream port AER register discovery
> PCI/AER: Refactor cper_print_aer() for use by CXL driver module
> cxl/pci: Update CXL error logging to use RAS register address
> cxl/pci: Prepare for logging RCH downstream port protocol errors
> cxl/pci: Add RCH downstream port error logging
>
> base-commit: a70fc4ed20a6118837b0aecbbf789074935f473b
>
> drivers/cxl/acpi.c | 191 +++++++++++++++++++---------------
> drivers/cxl/core/hdm.c | 59 +++++------
> drivers/cxl/core/pci.c | 140 ++++++++++++++++++++++---
> drivers/cxl/core/port.c | 157 ++++++++++++++++++++++++----
> drivers/cxl/core/region.c | 4 +-
> drivers/cxl/core/regs.c | 152 ++++++++++++++++++++++++---
> drivers/cxl/cxl.h | 56 ++++++----
> drivers/cxl/cxlmem.h | 5 +-
> drivers/cxl/mem.c | 16 +--
> drivers/cxl/pci.c | 109 +++++++------------
> drivers/cxl/port.c | 5 +-
> drivers/pci/pcie/Kconfig | 12 +++
> drivers/pci/pcie/aer.c | 173 ++++++++++++++++++++++++++++--
> include/linux/aer.h | 2 +-
> tools/testing/cxl/Kbuild | 2 +-
> tools/testing/cxl/test/cxl.c | 10 +-
> tools/testing/cxl/test/mock.c | 12 +--
> tools/testing/cxl/test/mock.h | 7 +-
> 18 files changed, 824 insertions(+), 288 deletions(-)
>
From: Robert Richter <[email protected]>
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 1 -
drivers/cxl/cxl.h | 2 --
2 files changed, 3 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 183f9f8548e2..d147f08780d0 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -987,7 +987,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->dev = dport_dev;
dport->port_id = port_id;
- dport->component_reg_phys = component_reg_phys;
dport->port = port;
dport->rcrb.base = rcrb;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 4365d46606df..6134644b51f8 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -601,7 +601,6 @@ struct cxl_rcrb_info {
* @port: reference to cxl_port that contains this downstream port
* @comp_map: component register capability mappings
* @port_id: unique hardware identifier for dport in decoder target list
- * @component_reg_phys: downstream port component registers
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
* @rcrb: Data about the Root Complex Register Block layout
*/
@@ -610,7 +609,6 @@ struct cxl_dport {
struct cxl_port *port;
struct cxl_register_map comp_map;
int port_id;
- resource_size_t component_reg_phys;
bool rch;
struct cxl_rcrb_info rcrb;
};
--
2.34.1
From: Robert Richter <[email protected]>
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/cxlmem.h | 2 --
drivers/cxl/mem.c | 4 ++--
drivers/cxl/pci.c | 3 ---
3 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 2823c5aaf3db..7b5b2d3187bf 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -287,7 +287,6 @@ struct cxl_poison_state {
* @active_persistent_bytes: sum of hard + soft persistent
* @next_volatile_bytes: volatile capacity change pending device reset
* @next_persistent_bytes: persistent capacity change pending device reset
- * @component_reg_phys: register base of component registers
* @info: Cached DVSEC information about the device.
* @serial: PCIe Device Serial Number
* @event: event log driver state
@@ -326,7 +325,6 @@ struct cxl_dev_state {
u64 next_volatile_bytes;
u64 next_persistent_bytes;
- resource_size_t component_reg_phys;
u64 serial;
struct cxl_event_state event;
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 0643852444f3..618e839919eb 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -49,7 +49,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
struct cxl_dport *parent_dport)
{
struct cxl_port *parent_port = parent_dport->port;
- struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *endpoint, *iter, *down;
int rc;
@@ -65,8 +64,9 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
ep->next = down;
}
+ /* The Endpoint's component regs are located in cxlds. */
endpoint = devm_cxl_add_port(host, &cxlmd->dev,
- cxlds->component_reg_phys,
+ CXL_RESOURCE_NONE,
parent_dport);
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 00983770ea7b..0db71493db5d 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -661,7 +661,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* If the component registers can't be found, the cxl_pci driver may
* still be useful for management functions so don't return an error.
*/
- cxlds->component_reg_phys = CXL_RESOURCE_NONE;
rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
&cxlds->comp_map);
if (rc)
@@ -669,8 +668,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
else if (!cxlds->comp_map.component_map.ras.valid)
dev_dbg(&pdev->dev, "RAS registers not found\n");
- cxlds->component_reg_phys = cxlds->comp_map.resource;
-
rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
--
2.34.1
From: Robert Richter <[email protected]>
In Restricted CXL Device (RCD) mode a CXL device is exposed as an
RCiEP, but CXL downstream and upstream ports are not enumerated and
not visible in the PCIe hierarchy. Protocol and link errors are sent
to an RCEC.
Restricted CXL host (RCH) downstream port-detected errors are signaled
as internal AER errors, either Uncorrectable Internal Error (UIE) or
Corrected Internal Errors (CIE). The error source is the id of the
RCEC. A CXL handler must then inspect the error status in various CXL
registers residing in the dport's component register space (CXL RAS
capability) or the dport's RCRB (PCIe AER extended capability). [1]
Errors showing up in the RCEC's error handler must be handled and
connected to the CXL subsystem. Implement this by forwarding the error
to all CXL devices below the RCEC. Since the entire CXL device is
controlled only using PCIe Configuration Space of device 0, function
0, only pass it there [2]. The error handling is limited to currently
supported devices with the Memory Device class code set
(PCI_CLASS_MEMORY_CXL, 502h), where the handler can be implemented in
the existing cxl_pci driver. Support of CXL devices (e.g. a CXL.cache
device) can be enabled later.
In addition to errors directed to the CXL endpoint device, a handler
must also inspect the CXL RAS and PCIe AER capabilities of the CXL
downstream port that is connected to the device.
Since CXL downstream port errors are signaled using internal errors,
the handler requires those errors to be unmasked. This is subject of a
follow-on patch.
The reason for choosing this implementation is that a CXL RCEC device
is bound to the AER port driver, but the driver does not allow it to
register a custom specific handler to support CXL. Connecting the RCEC
hard-wired with a CXL handler does not work, as the CXL subsystem
might not be present all the time. The alternative to add an
implementation to the portdrv to allow the registration of a custom
RCEC error handler isn't worth doing it as CXL would be its only user.
Instead, just check for an CXL RCEC and pass it down to the connected
CXL device's error handler. With this approach the code can entirely
be implemented in the PCIe AER driver and is independent of the CXL
subsystem. The CXL driver only provides the handler.
[1] CXL 3.0 spec, 12.2.1.1 RCH Downstream Port-detected Errors
[2] CXL 3.0 spec, 8.1.3 PCIe DVSEC for CXL Devices
Co-developed-by: Terry Bowman <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Cc: "Oliver O'Halloran" <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
Cc: [email protected]
Cc: [email protected]
---
drivers/pci/pcie/Kconfig | 12 +++++
drivers/pci/pcie/aer.c | 100 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 110 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 228652a59f27..4f0e70fafe2d 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -49,6 +49,18 @@ config PCIEAER_INJECT
gotten from:
https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/
+config PCIEAER_CXL
+ bool "PCI Express CXL RAS support for Restricted Hosts (RCH)"
+ default y
+ depends on PCIEAER && CXL_PCI
+ help
+ Enables error handling of downstream ports of a CXL host
+ that is operating in RCD mode (Restricted CXL Host, RCH).
+ The downstream port reports AER errors to a given RCEC.
+ Errors are handled by the CXL memory device driver.
+
+ If unsure, say Y.
+
#
# PCI Express ECRC
#
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index d3344fcf1f79..2e3f00b6a5bd 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -946,14 +946,104 @@ static bool find_source_device(struct pci_dev *parent,
return true;
}
+#ifdef CONFIG_PCIEAER_CXL
+
+static bool is_cxl_mem_dev(struct pci_dev *dev)
+{
+ /*
+ * The capability, status, and control fields in Device 0,
+ * Function 0 DVSEC control the CXL functionality of the
+ * entire device (CXL 3.0, 8.1.3).
+ */
+ if (dev->devfn != PCI_DEVFN(0, 0))
+ return false;
+
+ /*
+ * CXL Memory Devices must have the 502h class code set (CXL
+ * 3.0, 8.1.12.1).
+ */
+ if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL)
+ return false;
+
+ return true;
+}
+
+static bool cxl_error_is_native(struct pci_dev *dev)
+{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+
+ if (pcie_ports_native)
+ return true;
+
+ return host->native_aer && host->native_cxl_error;
+}
+
+static bool is_internal_error(struct aer_err_info *info)
+{
+ if (info->severity == AER_CORRECTABLE)
+ return info->status & PCI_ERR_COR_INTERNAL;
+
+ return info->status & PCI_ERR_UNC_INTN;
+}
+
+static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
+{
+ struct aer_err_info *info = (struct aer_err_info *)data;
+ const struct pci_error_handlers *err_handler;
+
+ if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
+ return 0;
+
+ /* protect dev->driver */
+ device_lock(&dev->dev);
+
+ err_handler = dev->driver ? dev->driver->err_handler : NULL;
+ if (!err_handler)
+ goto out;
+
+ if (info->severity == AER_CORRECTABLE) {
+ if (err_handler->cor_error_detected)
+ err_handler->cor_error_detected(dev);
+ } else if (err_handler->error_detected) {
+ if (info->severity == AER_NONFATAL)
+ err_handler->error_detected(dev, pci_channel_io_normal);
+ else if (info->severity == AER_FATAL)
+ err_handler->error_detected(dev, pci_channel_io_frozen);
+ }
+out:
+ device_unlock(&dev->dev);
+ return 0;
+}
+
+static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ /*
+ * CXL downstream ports of a CXL host that is operating in RCD
+ * mode (RCH) signal errors as RCEC internal errors. Forward
+ * them to all CXL devices below the RCEC.
+ *
+ * See CXL 3.0:
+ * 9.11.8 CXL Devices Attached to an RCH
+ * 12.2.1.1 RCH Downstream Port-detected Errors
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
+ is_internal_error(info))
+ pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
+}
+
+#else
+static inline void cxl_rch_handle_error(struct pci_dev *dev,
+ struct aer_err_info *info) { }
+#endif
+
/**
- * handle_error_source - handle logging error into an event log
+ * pci_aer_handle_error - handle logging error into an event log
* @dev: pointer to pci_dev data structure of error source device
* @info: comprehensive error information
*
* Invoked when an error being detected by Root Port.
*/
-static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
+static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
{
int aer = dev->aer_cap;
@@ -977,6 +1067,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset);
else if (info->severity == AER_FATAL)
pcie_do_recovery(dev, pci_channel_io_frozen, aer_root_reset);
+}
+
+static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
+{
+ cxl_rch_handle_error(dev, info);
+ pci_aer_handle_error(dev, info);
pci_dev_put(dev);
}
--
2.34.1
The CXL driver plans to use cper_print_aer() for logging restricted CXL
host (RCH) AER errors. cper_print_aer() is not currently exported and
therefore not usable by the CXL driver built as a loadable module. Export
the cper_print_aer() function making it available.
The CONFIG_ACPI_APEI_PCIEAER kernel config is currently used to enable
cper_print_aer(). cper_print_aer() logs the AER registers and is
useful in PCIE AER logging outside of APEI. Remove the
CONFIG_ACPI_APEI_PCIEAER dependency to enable cper_print_aer().
The cper_print_aer() function name implies CPER specific use but is useful
in non-CPER cases as well. Rename cper_print_aer() to pci_print_aer().
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Cc: Mahesh J Salgaonkar <[email protected]>
Cc: "Oliver O'Halloran" <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
Cc: [email protected]
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/pci/pcie/aer.c | 9 +++++----
include/linux/aer.h | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index f6c24ded134c..d3344fcf1f79 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -771,9 +771,10 @@ int cper_severity_to_aer(int cper_severity)
}
}
EXPORT_SYMBOL_GPL(cper_severity_to_aer);
+#endif
-void cper_print_aer(struct pci_dev *dev, int aer_severity,
- struct aer_capability_regs *aer)
+void pci_print_aer(struct pci_dev *dev, int aer_severity,
+ struct aer_capability_regs *aer)
{
int layer, agent, tlp_header_valid = 0;
u32 status, mask;
@@ -812,7 +813,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
trace_aer_event(dev_name(&dev->dev), (status & ~mask),
aer_severity, tlp_header_valid, &aer->header_log);
}
-#endif
+EXPORT_SYMBOL_NS_GPL(pci_print_aer, CXL);
/**
* add_error_device - list device to be handled
@@ -1009,7 +1010,7 @@ static void aer_recover_work_func(struct work_struct *work)
PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
continue;
}
- cper_print_aer(pdev, entry.severity, entry.regs);
+ pci_print_aer(pdev, entry.severity, entry.regs);
if (entry.severity == AER_NONFATAL)
pcie_do_recovery(pdev, pci_channel_io_normal,
aer_root_reset);
diff --git a/include/linux/aer.h b/include/linux/aer.h
index 97f64ba1b34a..8f124b904314 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -64,7 +64,7 @@ static inline void pci_save_aer_state(struct pci_dev *dev) {}
static inline void pci_restore_aer_state(struct pci_dev *dev) {}
#endif
-void cper_print_aer(struct pci_dev *dev, int aer_severity,
+void pci_print_aer(struct pci_dev *dev, int aer_severity,
struct aer_capability_regs *aer);
int cper_severity_to_aer(int cper_severity);
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
--
2.34.1
From: Robert Richter <[email protected]>
Same as for ports and dports, also store the endpoint's Component
Register mappings, use struct cxl_dev_state for that.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/cxlmem.h | 3 ++-
drivers/cxl/pci.c | 9 +++++----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index a2845a7a69d8..2823c5aaf3db 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -263,6 +263,7 @@ struct cxl_poison_state {
*
* @dev: The device associated with this CXL state
* @cxlmd: The device representing the CXL.mem capabilities of @dev
+ * @comp_map: component register capability mappings
* @regs: Parsed register blocks
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
@@ -299,7 +300,7 @@ struct cxl_poison_state {
struct cxl_dev_state {
struct device *dev;
struct cxl_memdev *cxlmd;
-
+ struct cxl_register_map comp_map;
struct cxl_regs regs;
int cxl_dvsec;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 54c486cd65dd..00983770ea7b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -662,15 +662,16 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* still be useful for management functions so don't return an error.
*/
cxlds->component_reg_phys = CXL_RESOURCE_NONE;
- rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
+ &cxlds->comp_map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
- else if (!map.component_map.ras.valid)
+ else if (!cxlds->comp_map.component_map.ras.valid)
dev_dbg(&pdev->dev, "RAS registers not found\n");
- cxlds->component_reg_phys = map.resource;
+ cxlds->component_reg_phys = cxlds->comp_map.resource;
- rc = cxl_map_component_regs(&map, &cxlds->regs.component,
+ rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
--
2.34.1
From: Robert Richter <[email protected]>
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 4 +---
drivers/cxl/cxl.h | 2 --
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index db2ba0c886e2..183f9f8548e2 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -615,7 +615,6 @@ static int devm_cxl_link_parent_dport(struct device *host,
static struct lock_class_key cxl_port_key;
static struct cxl_port *cxl_port_alloc(struct device *uport,
- resource_size_t component_reg_phys,
struct cxl_dport *parent_dport)
{
struct cxl_port *port;
@@ -665,7 +664,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
} else
dev->parent = uport;
- port->component_reg_phys = component_reg_phys;
ida_init(&port->decoder_ida);
port->hdm_end = -1;
port->commit_end = -1;
@@ -724,7 +722,7 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *dev;
int rc;
- port = cxl_port_alloc(uport, component_reg_phys, parent_dport);
+ port = cxl_port_alloc(uport, parent_dport);
if (IS_ERR(port))
return port;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index dc83c1d0396e..4365d46606df 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -556,7 +556,6 @@ struct cxl_dax_region {
* @nr_dports: number of entries in @dports
* @hdm_end: track last allocated HDM decoder instance for allocation ordering
* @commit_end: cursor to track highest committed decoder for commit ordering
- * @component_reg_phys: component register capability base address (optional)
* @dead: last ep has been removed, force port re-creation
* @depth: How deep this port is relative to the root. depth 0 is the root.
* @cdat: Cached CDAT data
@@ -576,7 +575,6 @@ struct cxl_port {
int nr_dports;
int hdm_end;
int commit_end;
- resource_size_t component_reg_phys;
bool dead;
unsigned int depth;
struct cxl_cdat {
--
2.34.1
The CXL error handler currently only logs endpoint RAS status. The CXL
topology includes several components providing RAS details to be logged
during error handling.[1] Update the current handler's RAS logging to use a
RAS register address. This will allow for adding support to log other CXL
component's RAS details in the future.
[1] CXL3.0 Table 8-22 CXL_Capability_ID Assignment
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/pci.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 67f4ab6daa34..def6ee5ab4f5 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -665,32 +665,36 @@ void read_cdat_data(struct cxl_port *port)
}
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
-void cxl_cor_error_detected(struct pci_dev *pdev)
+static void __cxl_log_correctable_ras(struct cxl_dev_state *cxlds,
+ void __iomem *ras_base)
{
- struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
void __iomem *addr;
u32 status;
- if (!cxlds->regs.ras)
+ if (!ras_base)
return;
- addr = cxlds->regs.ras + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
+ addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
status = readl(addr);
if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) {
writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
trace_cxl_aer_correctable_error(cxlds->cxlmd, status);
}
}
-EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
+
+static void cxl_log_correctable_ras_endpoint(struct cxl_dev_state *cxlds)
+{
+ return __cxl_log_correctable_ras(cxlds, cxlds->regs.ras);
+}
/* CXL spec rev3.0 8.2.4.16.1 */
-static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
+static void header_log_copy(void __iomem *ras_base, u32 *log)
{
void __iomem *addr;
u32 *log_addr;
int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32);
- addr = cxlds->regs.ras + CXL_RAS_HEADER_LOG_OFFSET;
+ addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET;
log_addr = log;
for (i = 0; i < log_u32_size; i++) {
@@ -704,17 +708,18 @@ static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
* Log the state of the RAS status registers and prepare them to log the
* next error status. Return 1 if reset needed.
*/
-static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
+static bool __cxl_report_and_clear(struct cxl_dev_state *cxlds,
+ void __iomem *ras_base)
{
u32 hl[CXL_HEADERLOG_SIZE_U32];
void __iomem *addr;
u32 status;
u32 fe;
- if (!cxlds->regs.ras)
+ if (!ras_base)
return false;
- addr = cxlds->regs.ras + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET;
+ addr = ras_base + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET;
status = readl(addr);
if (!(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK))
return false;
@@ -722,7 +727,7 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
/* If multiple errors, log header points to first error from ctrl reg */
if (hweight32(status) > 1) {
void __iomem *rcc_addr =
- cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
+ ras_base + CXL_RAS_CAP_CONTROL_OFFSET;
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
readl(rcc_addr)));
@@ -730,13 +735,26 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
fe = status;
}
- header_log_copy(cxlds, hl);
+ header_log_copy(ras_base, hl);
trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, hl);
writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
return true;
}
+static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
+{
+ return __cxl_report_and_clear(cxlds, cxlds->regs.ras);
+}
+
+void cxl_cor_error_detected(struct pci_dev *pdev)
+{
+ struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
+
+ cxl_log_correctable_ras_endpoint(cxlds);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
+
pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
--
2.34.1
From: Robert Richter <[email protected]>
AER corrected and uncorrectable internal errors (CIE/UIE) are masked
in their corresponding mask registers per default once in power-up
state. [1][2] Enable internal errors for RCECs to receive CXL
downstream port errors of Restricted CXL Hosts (RCHs).
[1] CXL 3.0 Spec, 12.2.1.1 - RCH Downstream Port Detected Errors
[2] PCIe Base Spec 6.0, 7.8.4.3 Uncorrectable Error Mask Register,
7.8.4.6 Correctable Error Mask Register
Co-developed-by: Terry Bowman <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
---
drivers/pci/pcie/aer.c | 64 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 2e3f00b6a5bd..c5076ae4eb58 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -948,6 +948,32 @@ static bool find_source_device(struct pci_dev *parent,
#ifdef CONFIG_PCIEAER_CXL
+static int pci_aer_unmask_internal_errors(struct pci_dev *dev)
+{
+ int aer, rc;
+ u32 mask;
+
+ if (!pcie_aer_is_native(dev))
+ return -EIO;
+
+ aer = dev->aer_cap;
+ rc = pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
+ if (rc)
+ return rc;
+ mask &= ~PCI_ERR_UNC_INTN;
+ rc = pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
+ if (rc)
+ return rc;
+
+ rc = pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
+ if (rc)
+ return rc;
+ mask &= ~PCI_ERR_COR_INTERNAL;
+ rc = pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
+
+ return rc;
+}
+
static bool is_cxl_mem_dev(struct pci_dev *dev)
{
/*
@@ -1031,7 +1057,44 @@ static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
}
+static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
+{
+ int *handles_cxl = data;
+
+ *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
+
+ return *handles_cxl;
+}
+
+static bool handles_cxl_errors(struct pci_dev *rcec)
+{
+ int handles_cxl = 0;
+
+ if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
+ pcie_aer_is_native(rcec))
+ pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
+
+ return !!handles_cxl;
+}
+
+static void cxl_rch_enable_rcec(struct pci_dev *rcec)
+{
+ if (!handles_cxl_errors(rcec))
+ return;
+
+ /*
+ * Internal errors are masked by default, unmask RCEC's here
+ * PCI6.0 7.8.4.3 Uncorrectable Error Mask Register (Offset 08h)
+ * PCI6.0 7.8.4.6 Correctable Error Mask Register (Offset 14h)
+ */
+ if (pci_aer_unmask_internal_errors(rcec))
+ pci_err(rcec, "CXL: Failed to unmask internal errors");
+ else
+ pci_info(rcec, "CXL: Internal errors unmasked");
+}
+
#else
+static inline void cxl_rch_enable_rcec(struct pci_dev *dev) { }
static inline void cxl_rch_handle_error(struct pci_dev *dev,
struct aer_err_info *info) { }
#endif
@@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
return status;
}
+ cxl_rch_enable_rcec(port);
aer_enable_rootport(rpc);
pci_info(port, "enabled with IRQ %d\n", dev->irq);
return 0;
--
2.34.1
Restricted CXL host (RCH) downstream port AER information is not currently
logged while in the error state. One problem preventing the error logging
is the AER and RAS registers are not accessible. The CXL driver requires
changes to find RCH downstream port AER and RAS registers for purpose of
error logging.
RCH downstream ports are not enumerated during a PCI bus scan and are
instead discovered using system firmware, ACPI in this case.[1] The
downstream port is implemented as a Root Complex Register Block (RCRB).
The RCRB is a 4k memory block containing PCIe registers based on the PCIe
root port.[2] The RCRB includes AER extended capability registers used for
reporting errors. Note, the RCH's AER Capability is located in the RCRB
memory space instead of PCI configuration space, thus its register access
is different. Existing kernel PCIe AER functions can not be used to manage
the downstream port AER capabilities and RAS registers because the port was
not enumerated during PCI scan and the registers are not PCI config
accessible.
Discover RCH downstream port AER extended capability registers. Use MMIO
accesses to search for extended AER capability in RCRB register space.
[1] CXL 3.0 Spec, 9.11.2 - System Firmware View of CXL 1.1 Hierarchy
[2] CXL 3.0 Spec, 8.2.1.1 - RCH Downstream Port RCRB
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/regs.c | 51 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 7e56ddf509c0..045abc11add8 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -404,6 +404,54 @@ int cxl_setup_regs(struct cxl_register_map *map)
}
EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
+static void __iomem *cxl_map_reg(struct device *dev, resource_size_t addr,
+ resource_size_t length)
+{
+ struct resource *res;
+
+ if (WARN_ON_ONCE(addr == CXL_RESOURCE_NONE))
+ return NULL;
+
+ res = request_mem_region(addr, length, dev_name(dev));
+ if (!res)
+ return NULL;
+
+ return ioremap(addr, length);
+}
+
+static void cxl_unmap_reg(void __iomem *base, resource_size_t addr,
+ resource_size_t length)
+{
+ iounmap(base);
+ release_mem_region(addr, length);
+}
+
+static u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb)
+{
+ void __iomem *addr;
+ u16 offset = 0;
+ u32 cap_hdr;
+
+ addr = cxl_map_reg(dev, rcrb, SZ_4K);
+ if (!addr)
+ return 0;
+
+ cap_hdr = readl(addr + offset);
+ while (PCI_EXT_CAP_ID(cap_hdr) != PCI_EXT_CAP_ID_ERR) {
+ offset = PCI_EXT_CAP_NEXT(cap_hdr);
+ if (!offset)
+ break;
+ cap_hdr = readl(addr + offset);
+ }
+
+ if (offset)
+ dev_dbg(dev, "found AER extended capability (0x%x)\n", offset);
+
+ cxl_unmap_reg(addr, rcrb, SZ_4K);
+
+ return offset;
+}
+
resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
struct cxl_rcrb_info *ri, enum cxl_rcrb which)
{
@@ -467,6 +515,9 @@ resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
if (!IS_ALIGNED(component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE))
return CXL_RESOURCE_NONE;
+ if (ri)
+ ri->aer_cap = cxl_rcrb_to_aer(dev, ri->base);
+
return component_reg_phys;
}
EXPORT_SYMBOL_NS_GPL(cxl_probe_rcrb, CXL);
--
2.34.1
RCH downstream port error logging is missing in the current CXL driver. The
missing AER and RAS error logging is needed for communicating driver error
details to userspace. Update the driver to include PCIe AER and CXL RAS
error logging.
Add RCH downstream port error handling into the existing RCiEP handler.
The downstream port error handler is added to the RCiEP error handler
because the downstream port is implemented in a RCRB, is not PCI
enumerable, and as a result is not directly accessible to the PCI AER
root port driver. The AER root port driver calls the RCiEP handler for
handling RCD errors and RCH downstream port protocol errors.
Update existing RCiEP correctable and uncorrectable handlers to also call
the RCH handler. The RCH handler will read the RCH AER registers, check for
error severity, and if an error exists will log using an existing kernel
AER trace routine. The RCH handler will also log downstream port RAS errors
if they exist.
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/pci.c | 98 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index def6ee5ab4f5..97886aacc64a 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
+#include <linux/aer.h>
#include <cxlpci.h>
#include <cxlmem.h>
#include <cxl.h>
@@ -747,10 +748,105 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
return __cxl_report_and_clear(cxlds, cxlds->regs.ras);
}
+#ifdef CONFIG_PCIEAER_CXL
+
+static void cxl_log_correctable_ras_dport(struct cxl_dev_state *cxlds,
+ struct cxl_dport *dport)
+{
+ return __cxl_log_correctable_ras(cxlds, dport->regs.ras);
+}
+
+static bool cxl_report_and_clear_dport(struct cxl_dev_state *cxlds,
+ struct cxl_dport *dport)
+{
+ return __cxl_report_and_clear(cxlds, dport->regs.ras);
+}
+
+/*
+ * Copy the AER capability registers using 32 bit read accesses.
+ * This is necessary because RCRB AER capability is MMIO mapped. Clear the
+ * status after copying.
+ *
+ * @aer_base: base address of AER capability block in RCRB
+ * @aer_regs: destination for copying AER capability
+ */
+static bool cxl_rch_get_aer_info(void __iomem *aer_base,
+ struct aer_capability_regs *aer_regs)
+{
+ int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32);
+ u32 *aer_regs_buf = (u32 *)aer_regs;
+ int n;
+
+ if (!aer_base)
+ return false;
+
+ /* Use readl() to guarantee 32-bit accesses */
+ for (n = 0; n < read_cnt; n++)
+ aer_regs_buf[n] = readl(aer_base + n * sizeof(u32));
+
+ writel(aer_regs->uncor_status, aer_base + PCI_ERR_UNCOR_STATUS);
+ writel(aer_regs->cor_status, aer_base + PCI_ERR_COR_STATUS);
+
+ return true;
+}
+
+/* Get AER severity. Return false if there is no error. */
+static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
+ int *severity)
+{
+ if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
+ if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
+ *severity = AER_FATAL;
+ else
+ *severity = AER_NONFATAL;
+ return true;
+ }
+
+ if (aer_regs->cor_status & ~aer_regs->cor_mask) {
+ *severity = AER_CORRECTABLE;
+ return true;
+ }
+
+ return false;
+}
+
+static void cxl_handle_rch_dport_errors(struct cxl_dev_state *cxlds)
+{
+ struct pci_dev *pdev = to_pci_dev(cxlds->dev);
+ struct aer_capability_regs aer_regs;
+ struct cxl_dport *dport;
+ int severity;
+
+ if (!cxlds->rcd)
+ return;
+
+ if (!cxl_pci_find_port(pdev, &dport) || !dport->rch)
+ return;
+
+ if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
+ return;
+
+ if (!cxl_rch_get_aer_severity(&aer_regs, &severity))
+ return;
+
+ pci_print_aer(pdev, severity, &aer_regs);
+
+ if (severity == AER_CORRECTABLE)
+ cxl_log_correctable_ras_dport(cxlds, dport);
+ else
+ cxl_report_and_clear_dport(cxlds, dport);
+}
+
+#else
+static void cxl_handle_rch_dport_errors(struct cxl_dev_state *cxlds) { }
+#endif
+
void cxl_cor_error_detected(struct pci_dev *pdev)
{
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
+ cxl_handle_rch_dport_errors(cxlds);
+
cxl_log_correctable_ras_endpoint(cxlds);
}
EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
@@ -763,6 +859,8 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
struct device *dev = &cxlmd->dev;
bool ue;
+ cxl_handle_rch_dport_errors(cxlds);
+
/*
* A frozen channel indicates an impending reset which is fatal to
* CXL.mem operation, and will likely crash the system. On the off
--
2.34.1
Hi Terry,
kernel test robot noticed the following build warnings:
[auto build test WARNING on a70fc4ed20a6118837b0aecbbf789074935f473b]
url: https://github.com/intel-lab-lkp/linux/commits/Terry-Bowman/cxl-acpi-Probe-RCRB-later-during-RCH-downstream-port-creation/20230524-073436
base: a70fc4ed20a6118837b0aecbbf789074935f473b
patch link: https://lore.kernel.org/r/20230523232214.55282-14-terry.bowman%40amd.com
patch subject: [PATCH v4 13/23] cxl/hdm: Use stored Component Register mappings to map HDM decoder capability
config: ia64-allyesconfig
compiler: ia64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/d41cd3b17998709dba2acc87016b07c0720e5b67
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Terry-Bowman/cxl-acpi-Probe-RCRB-later-during-RCH-downstream-port-creation/20230524-073436
git checkout d41cd3b17998709dba2acc87016b07c0720e5b67
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=ia64 SHELL=/bin/bash drivers/cxl/core/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/cxl/core/hdm.c:128:26: warning: no previous prototype for 'cxl_port_get_comp_map' [-Wmissing-prototypes]
128 | struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
| ^~~~~~~~~~~~~~~~~~~~~
vim +/cxl_port_get_comp_map +128 drivers/cxl/core/hdm.c
127
> 128 struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
129 {
130 /*
131 * HDM capability applies to Endpoints, USPs and VH Host
132 * Bridges. The Endpoint's component register mappings are
133 * located in the cxlds.
134 */
135 if (is_cxl_endpoint(port)) {
136 struct cxl_memdev *memdev = to_cxl_memdev(port->uport);
137
138 return &memdev->cxlds->comp_map;
139 }
140
141 return &port->comp_map;
142 }
143
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Dropped the change log somehow, Adding here.
Changes in V4:
- Made port RAS register discovery common and called from
__devm_cxl_add_dport().
- Changed RCH AER register discovery to be called from
__devm_cxl_add_dport().
- Changed RAS and RCH AER register mapping to be called from
__devm_cxl_add_dport().
- Changed component register mapping to use common mapping,
cxl_map_component_regs().
- Added cxl_regs to 'struct cxl_dport' for providing RCH downstream port
mapped registers used in error handler.
- Refactored cxl_map_reg() to be like devm_cxl_iomap_block().
- PCI/AER:
- Removed patch for cper_mem_err_unpack().
- Renamed cper_print_aer() to pci_print_aer().
- Changed pci_print_aer() export to use
EXPORT_SYMBOL_NS_GPL(pci_print_aer, CXL).
- Improved description of PCIEAER_CXL option in Kconfig.
- Renamed function to pci_aer_unmask_internal_errors(), added
pcie_aer_is_native() check.
- Improved comments and added spec refs.
- Renamed functions to cxl_rch_handle_error*().
- Modified cxl_rch_handle_error_iter() to only call the handler
callbacks, this also simplifies refcounting of the pdev.
- Refactored handle_error_source(), created pci_aer_handle_error().
- Changed printk messages to pci_*() variants.
- Added check for pcie_aer_is_native() to the RCEC.
- Introduced function cxl_rch_enable_rcec().
- Updated patch description ("PCI/AER: Forward RCH downstream
port-detected errors to the CXL.mem dev handler").
Changes in V3:
- Correct base commit in cover sheet.
- Change hardcoded return 0 to NULL in regs.c.
- Remove calls to pci_disable_pcie_error_reporting(pdev) and
pci_enable_pcie_error_reporting(pdev) in mem.c;
- Move RCEC interrupt unmask to PCIe port AER driver's probe.
- Fixes missing PCIEAER and PCIEPORTBUS config option error.
- Rename cxl_rcrb_setup() to cxl_setup_rcrb() in mem.c.
- Update cper_mem_err_unpack() patch subject and description.
Changes in V2:
- Refactor RCH initialization into cxl_mem driver.
- Includes RCH RAS and AER register discovery and mapping.
- Add RCEC protocol error interrupt forwarding to CXL endpoint
handler.
- Change AER and RAS logging to use existing trace routines.
- Enable RCEC AER internal errors.
Regards,
Terry
On 5/23/23 18:21, Terry Bowman wrote:
> Patches #1 to #16 are a rework of the Component Register setup. This
> is needed to share multiple CXL capabilities (HDM and RAS) for the
> same component, also there can be different components implementing
> the same capability, finally RCH mode should be supported too. The
> general approach to solve this is to:
>
> * Unify code for components and capabilities in VH and RCH modes.
>
> * Early setup of the Component Register base address.
>
> * Create and store the register mappings to later use it for mapping
> the capability I/O ranges.
>
> Patches #17 to #23 enable CXL RCH error handling. These are needed because
> RCH downstream port protocol error handling is implemented uniquely and not
> currently supported. These patches address the following:
>
> * Discovery and mapping of RCH downstream port AER registers.
>
> * AER portdrv changes to support CXL RCH protocol errors.
>
> * Interrupt setup specific to RCH mode: enabling RCEC internal
> errors and disabling root port interrupts.
>
> Dan Williams (1):
> cxl/rch: Prepare for caching the MMIO mapped PCIe AER capability
>
> Robert Richter (16):
> cxl/acpi: Probe RCRB later during RCH downstream port creation
> cxl: Rename member @dport of struct cxl_dport to @dev
> cxl/core/regs: Add @dev to cxl_register_map
> cxl/acpi: Moving add_host_bridge_uport() around
> cxl/acpi: Directly bind the CEDT detected CHBCR to the Host Bridge's
> port
> cxl/regs: Remove early capability checks in Component Register setup
> cxl/pci: Early setup RCH dport component registers from RCRB
> cxl/port: Store the port's Component Register mappings in struct
> cxl_port
> cxl/port: Store the downstream port's Component Register mappings in
> struct cxl_dport
> cxl/pci: Store the endpoint's Component Register mappings in struct
> cxl_dev_state
> cxl/hdm: Use stored Component Register mappings to map HDM decoder
> capability
> cxl/port: Remove Component Register base address from struct cxl_port
> cxl/port: Remove Component Register base address from struct cxl_dport
> cxl/pci: Remove Component Register base address from struct
> cxl_dev_state
> PCI/AER: Forward RCH downstream port-detected errors to the CXL.mem
> dev handler
> PCI/AER: Unmask RCEC internal errors to enable RCH downstream port
> error handling
>
> Terry Bowman (6):
> cxl/pci: Refactor component register discovery for reuse
> cxl/pci: Add RCH downstream port AER register discovery
> PCI/AER: Refactor cper_print_aer() for use by CXL driver module
> cxl/pci: Update CXL error logging to use RAS register address
> cxl/pci: Prepare for logging RCH downstream port protocol errors
> cxl/pci: Add RCH downstream port error logging
>
> base-commit: a70fc4ed20a6118837b0aecbbf789074935f473b
>
> drivers/cxl/acpi.c | 191 +++++++++++++++++++---------------
> drivers/cxl/core/hdm.c | 59 +++++------
> drivers/cxl/core/pci.c | 140 ++++++++++++++++++++++---
> drivers/cxl/core/port.c | 157 ++++++++++++++++++++++++----
> drivers/cxl/core/region.c | 4 +-
> drivers/cxl/core/regs.c | 152 ++++++++++++++++++++++++---
> drivers/cxl/cxl.h | 56 ++++++----
> drivers/cxl/cxlmem.h | 5 +-
> drivers/cxl/mem.c | 16 +--
> drivers/cxl/pci.c | 109 +++++++------------
> drivers/cxl/port.c | 5 +-
> drivers/pci/pcie/Kconfig | 12 +++
> drivers/pci/pcie/aer.c | 173 ++++++++++++++++++++++++++++--
> include/linux/aer.h | 2 +-
> tools/testing/cxl/Kbuild | 2 +-
> tools/testing/cxl/test/cxl.c | 10 +-
> tools/testing/cxl/test/mock.c | 12 +--
> tools/testing/cxl/test/mock.h | 7 +-
> 18 files changed, 824 insertions(+), 288 deletions(-)
>
On 24.05.23 09:12:24, kernel test robot wrote:
> >> drivers/cxl/core/hdm.c:128:26: warning: no previous prototype for 'cxl_port_get_comp_map' [-Wmissing-prototypes]
> 128 | struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
> | ^~~~~~~~~~~~~~~~~~~~~
>
>
> vim +/cxl_port_get_comp_map +128 drivers/cxl/core/hdm.c
>
> 127
> > 128 struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
This should have been 'static' and also solves the warning then. Will
change.
-Robert
> 129 {
> 130 /*
> 131 * HDM capability applies to Endpoints, USPs and VH Host
> 132 * Bridges. The Endpoint's component register mappings are
> 133 * located in the cxlds.
> 134 */
> 135 if (is_cxl_endpoint(port)) {
> 136 struct cxl_memdev *memdev = to_cxl_memdev(port->uport);
> 137
> 138 return &memdev->cxlds->comp_map;
> 139 }
> 140
> 141 return &port->comp_map;
> 142 }
> 143
On Tue, May 23, 2023 at 06:22:09PM -0500, Terry Bowman wrote:
> The CXL driver plans to use cper_print_aer() for logging restricted CXL
> host (RCH) AER errors. cper_print_aer() is not currently exported and
> therefore not usable by the CXL driver built as a loadable module. Export
> the cper_print_aer() function making it available.
>
> The CONFIG_ACPI_APEI_PCIEAER kernel config is currently used to enable
> cper_print_aer(). cper_print_aer() logs the AER registers and is
> useful in PCIE AER logging outside of APEI. Remove the
> CONFIG_ACPI_APEI_PCIEAER dependency to enable cper_print_aer().
>
> The cper_print_aer() function name implies CPER specific use but is useful
> in non-CPER cases as well. Rename cper_print_aer() to pci_print_aer().
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Cc: Mahesh J Salgaonkar <[email protected]>
> Cc: "Oliver O'Halloran" <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> Cc: [email protected]
> Reviewed-by: Jonathan Cameron <[email protected]>
Acked-by: Bjorn Helgaas <[email protected]>
> ---
> drivers/pci/pcie/aer.c | 9 +++++----
> include/linux/aer.h | 2 +-
> 2 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index f6c24ded134c..d3344fcf1f79 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -771,9 +771,10 @@ int cper_severity_to_aer(int cper_severity)
> }
> }
> EXPORT_SYMBOL_GPL(cper_severity_to_aer);
> +#endif
>
> -void cper_print_aer(struct pci_dev *dev, int aer_severity,
> - struct aer_capability_regs *aer)
> +void pci_print_aer(struct pci_dev *dev, int aer_severity,
> + struct aer_capability_regs *aer)
> {
> int layer, agent, tlp_header_valid = 0;
> u32 status, mask;
> @@ -812,7 +813,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
> trace_aer_event(dev_name(&dev->dev), (status & ~mask),
> aer_severity, tlp_header_valid, &aer->header_log);
> }
> -#endif
> +EXPORT_SYMBOL_NS_GPL(pci_print_aer, CXL);
>
> /**
> * add_error_device - list device to be handled
> @@ -1009,7 +1010,7 @@ static void aer_recover_work_func(struct work_struct *work)
> PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
> continue;
> }
> - cper_print_aer(pdev, entry.severity, entry.regs);
> + pci_print_aer(pdev, entry.severity, entry.regs);
> if (entry.severity == AER_NONFATAL)
> pcie_do_recovery(pdev, pci_channel_io_normal,
> aer_root_reset);
> diff --git a/include/linux/aer.h b/include/linux/aer.h
> index 97f64ba1b34a..8f124b904314 100644
> --- a/include/linux/aer.h
> +++ b/include/linux/aer.h
> @@ -64,7 +64,7 @@ static inline void pci_save_aer_state(struct pci_dev *dev) {}
> static inline void pci_restore_aer_state(struct pci_dev *dev) {}
> #endif
>
> -void cper_print_aer(struct pci_dev *dev, int aer_severity,
> +void pci_print_aer(struct pci_dev *dev, int aer_severity,
> struct aer_capability_regs *aer);
> int cper_severity_to_aer(int cper_severity);
> void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
> --
> 2.34.1
>
On Tue, May 23, 2023 at 06:22:13PM -0500, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> In Restricted CXL Device (RCD) mode a CXL device is exposed as an
> RCiEP, but CXL downstream and upstream ports are not enumerated and
> not visible in the PCIe hierarchy. Protocol and link errors are sent
> to an RCEC.
>
> Restricted CXL host (RCH) downstream port-detected errors are signaled
> as internal AER errors, either Uncorrectable Internal Error (UIE) or
> Corrected Internal Errors (CIE).
From the parallelism with RCD above, I first thought that RCH devices
were non-RCD mode and *were* enumerated as part of the PCIe hierarchy,
but actually I suspect it's more like the following?
... but CXL downstream and upstream ports are not enumerated and not
visible in the PCIe hierarchy.
Protocol and link errors from these non-enumerated ports are
signaled as internal AER errors ... via a CXL RCEC.
> The error source is the id of the RCEC.
This seems odd; I assume this refers to the RCEC's AER Error Source
Identification register, and the ERR_COR or ERR_FATAL/NONFATAL Source
Identification would ordinarily be the Requester ID of the RCiEP that
"sent" the Error Message. But you're saying it's actually the ID of
the *RCEC*, not the RCiEP?
We're going to call pci_aer_handle_error() as well, to handle the
non-internal errors, and I'm pretty sure that path expects the RCiEP
ID there.
Whatever the answer, I'm not sure this sentence is actually relevant
to this patch, since this patch doesn't read PCI_ERR_ROOT_ERR_SRC or
look at struct aer_err_source.id.
> A CXL handler must then inspect the error status in various CXL
> registers residing in the dport's component register space (CXL RAS
> capability) or the dport's RCRB (PCIe AER extended capability). [1]
>
> Errors showing up in the RCEC's error handler must be handled and
> connected to the CXL subsystem. Implement this by forwarding the error
> to all CXL devices below the RCEC. Since the entire CXL device is
> controlled only using PCIe Configuration Space of device 0, function
> 0, only pass it there [2]. The error handling is limited to currently
> supported devices with the Memory Device class code set
> (PCI_CLASS_MEMORY_CXL, 502h), where the handler can be implemented in
> the existing cxl_pci driver. Support of CXL devices (e.g. a CXL.cache
> device) can be enabled later.
I assume the Memory Devices are CXL devices, so maybe "Error handling
for *other* CXL devices ... can be enabled later"?
IIUC, this happens via cxl_rch_handle_error_iter() calling
pci_error_handlers for CXL RCiEPs. Maybe the is_cxl_mem_dev() check
belongs inside those handlers, since that driver claimed the RCiEP and
should know its functionality? Maybe is_internal_error() and
cxl_error_is_native(), too?
> In addition to errors directed to the CXL endpoint device, a handler
> must also inspect the CXL RAS and PCIe AER capabilities of the CXL
> downstream port that is connected to the device.
>
> Since CXL downstream port errors are signaled using internal errors,
> the handler requires those errors to be unmasked. This is subject of a
> follow-on patch.
>
> The reason for choosing this implementation is that a CXL RCEC device
> is bound to the AER port driver,
... is that the AER service driver claims the CXL RCEC device, but
does not allow registration of a CXL sub-service driver ...
> but the driver does not allow it to
> register a custom specific handler to support CXL. Connecting the RCEC
> hard-wired with a CXL handler does not work, as the CXL subsystem
> might not be present all the time. The alternative to add an
> implementation to the portdrv to allow the registration of a custom
> RCEC error handler isn't worth doing it as CXL would be its only user.
> Instead, just check for an CXL RCEC and pass it down to the connected
> CXL device's error handler. With this approach the code can entirely
> be implemented in the PCIe AER driver and is independent of the CXL
> subsystem. The CXL driver only provides the handler.
>
> [1] CXL 3.0 spec, 12.2.1.1 RCH Downstream Port-detected Errors
> [2] CXL 3.0 spec, 8.1.3 PCIe DVSEC for CXL Devices
>
> Co-developed-by: Terry Bowman <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Cc: "Oliver O'Halloran" <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
Given the questions are minor:
Acked-by: Bjorn Helgaas <[email protected]>
> ---
> drivers/pci/pcie/Kconfig | 12 +++++
> drivers/pci/pcie/aer.c | 100 ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 110 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
> index 228652a59f27..4f0e70fafe2d 100644
> --- a/drivers/pci/pcie/Kconfig
> +++ b/drivers/pci/pcie/Kconfig
> @@ -49,6 +49,18 @@ config PCIEAER_INJECT
> gotten from:
> https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/
>
> +config PCIEAER_CXL
> + bool "PCI Express CXL RAS support for Restricted Hosts (RCH)"
> + default y
> + depends on PCIEAER && CXL_PCI
> + help
> + Enables error handling of downstream ports of a CXL host
> + that is operating in RCD mode (Restricted CXL Host, RCH).
> + The downstream port reports AER errors to a given RCEC.
> + Errors are handled by the CXL memory device driver.
> +
> + If unsure, say Y.
> +
> #
> # PCI Express ECRC
> #
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index d3344fcf1f79..2e3f00b6a5bd 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -946,14 +946,104 @@ static bool find_source_device(struct pci_dev *parent,
> return true;
> }
>
> +#ifdef CONFIG_PCIEAER_CXL
> +
> +static bool is_cxl_mem_dev(struct pci_dev *dev)
> +{
> + /*
> + * The capability, status, and control fields in Device 0,
> + * Function 0 DVSEC control the CXL functionality of the
> + * entire device (CXL 3.0, 8.1.3).
> + */
> + if (dev->devfn != PCI_DEVFN(0, 0))
> + return false;
> +
> + /*
> + * CXL Memory Devices must have the 502h class code set (CXL
> + * 3.0, 8.1.12.1).
> + */
> + if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL)
> + return false;
> +
> + return true;
> +}
> +
> +static bool cxl_error_is_native(struct pci_dev *dev)
> +{
> + struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
> +
> + if (pcie_ports_native)
> + return true;
> +
> + return host->native_aer && host->native_cxl_error;
> +}
> +
> +static bool is_internal_error(struct aer_err_info *info)
> +{
> + if (info->severity == AER_CORRECTABLE)
> + return info->status & PCI_ERR_COR_INTERNAL;
> +
> + return info->status & PCI_ERR_UNC_INTN;
> +}
> +
> +static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
> +{
> + struct aer_err_info *info = (struct aer_err_info *)data;
> + const struct pci_error_handlers *err_handler;
> +
> + if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
> + return 0;
> +
> + /* protect dev->driver */
> + device_lock(&dev->dev);
> +
> + err_handler = dev->driver ? dev->driver->err_handler : NULL;
> + if (!err_handler)
> + goto out;
> +
> + if (info->severity == AER_CORRECTABLE) {
> + if (err_handler->cor_error_detected)
> + err_handler->cor_error_detected(dev);
> + } else if (err_handler->error_detected) {
> + if (info->severity == AER_NONFATAL)
> + err_handler->error_detected(dev, pci_channel_io_normal);
> + else if (info->severity == AER_FATAL)
> + err_handler->error_detected(dev, pci_channel_io_frozen);
> + }
> +out:
> + device_unlock(&dev->dev);
> + return 0;
> +}
> +
> +static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> +{
> + /*
> + * CXL downstream ports of a CXL host that is operating in RCD
> + * mode (RCH) signal errors as RCEC internal errors. Forward
> + * them to all CXL devices below the RCEC.
> + *
> + * See CXL 3.0:
> + * 9.11.8 CXL Devices Attached to an RCH
> + * 12.2.1.1 RCH Downstream Port-detected Errors
> + */
> + if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
> + is_internal_error(info))
> + pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
> +}
> +
> +#else
> +static inline void cxl_rch_handle_error(struct pci_dev *dev,
> + struct aer_err_info *info) { }
> +#endif
> +
> /**
> - * handle_error_source - handle logging error into an event log
> + * pci_aer_handle_error - handle logging error into an event log
> * @dev: pointer to pci_dev data structure of error source device
> * @info: comprehensive error information
> *
> * Invoked when an error being detected by Root Port.
> */
> -static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> +static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> {
> int aer = dev->aer_cap;
>
> @@ -977,6 +1067,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset);
> else if (info->severity == AER_FATAL)
> pcie_do_recovery(dev, pci_channel_io_frozen, aer_root_reset);
> +}
> +
> +static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> +{
> + cxl_rch_handle_error(dev, info);
> + pci_aer_handle_error(dev, info);
> pci_dev_put(dev);
> }
>
> --
> 2.34.1
>
On Tue, May 23, 2023 at 06:22:14PM -0500, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> AER corrected and uncorrectable internal errors (CIE/UIE) are masked
> in their corresponding mask registers per default once in power-up
> state. [1][2] Enable internal errors for RCECs to receive CXL
> downstream port errors of Restricted CXL Hosts (RCHs).
>
> [1] CXL 3.0 Spec, 12.2.1.1 - RCH Downstream Port Detected Errors
> [2] PCIe Base Spec 6.0, 7.8.4.3 Uncorrectable Error Mask Register,
> 7.8.4.6 Correctable Error Mask Register
I use "r6.0" to make sure it isn't mistaken for a section number.
> Co-developed-by: Terry Bowman <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> ---
> drivers/pci/pcie/aer.c | 64 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 64 insertions(+)
>
> diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> index 2e3f00b6a5bd..c5076ae4eb58 100644
> --- a/drivers/pci/pcie/aer.c
> +++ b/drivers/pci/pcie/aer.c
> @@ -948,6 +948,32 @@ static bool find_source_device(struct pci_dev *parent,
>
> #ifdef CONFIG_PCIEAER_CXL
>
> +static int pci_aer_unmask_internal_errors(struct pci_dev *dev)
> +{
> + int aer, rc;
> + u32 mask;
> +
> + if (!pcie_aer_is_native(dev))
> + return -EIO;
> +
> + aer = dev->aer_cap;
> + rc = pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
> + if (rc)
> + return rc;
I don't think there's much value in checking all these config accesses
for failure. A failure return really just means you called it with
invalid parameters; it doesn't tell you whether it was successful on
PCI.
> + mask &= ~PCI_ERR_UNC_INTN;
> + rc = pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
> + if (rc)
> + return rc;
> +
> + rc = pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
> + if (rc)
> + return rc;
> + mask &= ~PCI_ERR_COR_INTERNAL;
> + rc = pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
> +
> + return rc;
> +}
> +
> static bool is_cxl_mem_dev(struct pci_dev *dev)
> {
> /*
> @@ -1031,7 +1057,44 @@ static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
> }
>
> +static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
> +{
> + int *handles_cxl = data;
> +
> + *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
This effectively only looks at the *last* RCiEP associated with this
RCEC. I would expect a logical OR of all of them.
I see this is another use of is_cxl_mem_dev() and
cxl_error_is_native() that really requires them to be in this file.
> + return *handles_cxl;
> +}
> +
> +static bool handles_cxl_errors(struct pci_dev *rcec)
> +{
> + int handles_cxl = 0;
> +
> + if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
> + pcie_aer_is_native(rcec))
> + pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
> +
> + return !!handles_cxl;
> +}
> +
> +static void cxl_rch_enable_rcec(struct pci_dev *rcec)
> +{
> + if (!handles_cxl_errors(rcec))
> + return;
> +
> + /*
> + * Internal errors are masked by default, unmask RCEC's here
> + * PCI6.0 7.8.4.3 Uncorrectable Error Mask Register (Offset 08h)
> + * PCI6.0 7.8.4.6 Correctable Error Mask Register (Offset 14h)
The spec references seem superfluous here. The PCI_ERR_UNCOR_MASK and
PCI_ERR_COR_MASK in pci_aer_unmask_internal_errors() are pretty good
pointers.
> + */
> + if (pci_aer_unmask_internal_errors(rcec))
> + pci_err(rcec, "CXL: Failed to unmask internal errors");
> + else
> + pci_info(rcec, "CXL: Internal errors unmasked");
> +}
> +
> #else
> +static inline void cxl_rch_enable_rcec(struct pci_dev *dev) { }
> static inline void cxl_rch_handle_error(struct pci_dev *dev,
> struct aer_err_info *info) { }
> #endif
> @@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
> return status;
> }
>
> + cxl_rch_enable_rcec(port);
Could this be done by the driver that claims the CXL RCiEP? There's
no point in unmasking the errors before there's a driver with
pci_error_handlers that can do something with them anyway.
> aer_enable_rootport(rpc);
> pci_info(port, "enabled with IRQ %d\n", dev->irq);
> return 0;
> --
> 2.34.1
>
Hi Terry,
kernel test robot noticed the following build warnings:
[auto build test WARNING on a70fc4ed20a6118837b0aecbbf789074935f473b]
url: https://github.com/intel-lab-lkp/linux/commits/Terry-Bowman/cxl-acpi-Probe-RCRB-later-during-RCH-downstream-port-creation/20230524-073436
base: a70fc4ed20a6118837b0aecbbf789074935f473b
patch link: https://lore.kernel.org/r/20230523232214.55282-14-terry.bowman%40amd.com
patch subject: [PATCH v4 13/23] cxl/hdm: Use stored Component Register mappings to map HDM decoder capability
config: x86_64-randconfig-s042-20230524
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/d41cd3b17998709dba2acc87016b07c0720e5b67
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Terry-Bowman/cxl-acpi-Probe-RCRB-later-during-RCH-downstream-port-creation/20230524-073436
git checkout d41cd3b17998709dba2acc87016b07c0720e5b67
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 olddefconfig
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/cxl/core/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
sparse warnings: (new ones prefixed by >>)
>> drivers/cxl/core/hdm.c:128:25: sparse: sparse: symbol 'cxl_port_get_comp_map' was not declared. Should it be static?
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
eOn 24.05.23 16:32:35, Bjorn Helgaas wrote:
> On Tue, May 23, 2023 at 06:22:13PM -0500, Terry Bowman wrote:
> > From: Robert Richter <[email protected]>
> >
> > In Restricted CXL Device (RCD) mode a CXL device is exposed as an
> > RCiEP, but CXL downstream and upstream ports are not enumerated and
> > not visible in the PCIe hierarchy. Protocol and link errors are sent
> > to an RCEC.
> >
> > Restricted CXL host (RCH) downstream port-detected errors are signaled
> > as internal AER errors, either Uncorrectable Internal Error (UIE) or
> > Corrected Internal Errors (CIE).
>
> From the parallelism with RCD above, I first thought that RCH devices
> were non-RCD mode and *were* enumerated as part of the PCIe hierarchy,
> but actually I suspect it's more like the following?
>
> ... but CXL downstream and upstream ports are not enumerated and not
> visible in the PCIe hierarchy.
>
> Protocol and link errors from these non-enumerated ports are
> signaled as internal AER errors ... via a CXL RCEC.
Exactly, except the RCEC is standard PCIe and also must not
necessarily on the same PCI bus as the CXL RCiEPs are.
>
> > The error source is the id of the RCEC.
>
> This seems odd; I assume this refers to the RCEC's AER Error Source
> Identification register, and the ERR_COR or ERR_FATAL/NONFATAL Source
> Identification would ordinarily be the Requester ID of the RCiEP that
> "sent" the Error Message. But you're saying it's actually the ID of
> the *RCEC*, not the RCiEP?
Right, the downstream port has it's own AER ext capability in
non-config (io mapped) RCRB regsister range. Errors originating from
there are signaled as internal AER errors via the RCEC *with* the
RCEC's Requester ID. Code walks through all associated CXL endpoints,
determines the dport and checks its AER.
There is also an RDPAS structure defined in CXL but that is only a
different way to provide the RCEC to dport association instead of
using the RCEC's Endpoint Association Extended Capability. In the end
we get all associated RCHs and check the AER of all their dports.
The upstream port is signaled using the RCiEP's AER. CXL spec is
strict here: "Upstream Port RCRB shall not implement the AER Extended
Capability." The RCiEP's requestor ID is used then and it's config
space the AER is in.
CXL.cachemem errors are reported with the RCiEP as requester
too. Status is in the CXL RAS cap and the UIE or CIE is set
respectively in the AER status of the RCiEP.
>
> We're going to call pci_aer_handle_error() as well, to handle the
> non-internal errors, and I'm pretty sure that path expects the RCiEP
> ID there.
>
> Whatever the answer, I'm not sure this sentence is actually relevant
> to this patch, since this patch doesn't read PCI_ERR_ROOT_ERR_SRC or
> look at struct aer_err_source.id.
The source id is used in aer_process_err_devices() which finally calls
handle_error_source() for the device with the requestor id. This is
the place where cxl_rch_handle_error() checks if it is an RCEC that
recieved an internal error and has cxl devices connected to it. Then,
the request is forwarded to the cxl_mem handler which also needs to
check the dport now. That is, pcie_walk_rcec() in
cxl_rch_handle_error() is called with the RCEC's pci handle,
cxl_rch_handle_error_iter() with the RCiEP's pci handle..
>
> > A CXL handler must then inspect the error status in various CXL
> > registers residing in the dport's component register space (CXL RAS
> > capability) or the dport's RCRB (PCIe AER extended capability). [1]
> >
> > Errors showing up in the RCEC's error handler must be handled and
> > connected to the CXL subsystem. Implement this by forwarding the error
> > to all CXL devices below the RCEC. Since the entire CXL device is
> > controlled only using PCIe Configuration Space of device 0, function
> > 0, only pass it there [2]. The error handling is limited to currently
> > supported devices with the Memory Device class code set
> > (PCI_CLASS_MEMORY_CXL, 502h), where the handler can be implemented in
> > the existing cxl_pci driver. Support of CXL devices (e.g. a CXL.cache
> > device) can be enabled later.
>
> I assume the Memory Devices are CXL devices, so maybe "Error handling
> for *other* CXL devices ... can be enabled later"?
>
> IIUC, this happens via cxl_rch_handle_error_iter() calling
> pci_error_handlers for CXL RCiEPs. Maybe the is_cxl_mem_dev() check
> belongs inside those handlers, since that driver claimed the RCiEP and
> should know its functionality? Maybe is_internal_error() and
> cxl_error_is_native(), too?
The check is outside the handlers on purpose. A corresponding handler
is needed, it is cxl_pci_driver, see the class code in
cxl_mem_pci_tbl. As the handler must handle other device's sources,
only aware drivers may be called here. Otherwise a device's error
handler could be called for errors there the source is the RCEC.
>
> > In addition to errors directed to the CXL endpoint device, a handler
> > must also inspect the CXL RAS and PCIe AER capabilities of the CXL
> > downstream port that is connected to the device.
> >
> > Since CXL downstream port errors are signaled using internal errors,
> > the handler requires those errors to be unmasked. This is subject of a
> > follow-on patch.
> >
> > The reason for choosing this implementation is that a CXL RCEC device
> > is bound to the AER port driver,
>
> ... is that the AER service driver claims the CXL RCEC device, but
> does not allow registration of a CXL sub-service driver ...
>
> > but the driver does not allow it to
> > register a custom specific handler to support CXL. Connecting the RCEC
> > hard-wired with a CXL handler does not work, as the CXL subsystem
> > might not be present all the time. The alternative to add an
> > implementation to the portdrv to allow the registration of a custom
> > RCEC error handler isn't worth doing it as CXL would be its only user.
> > Instead, just check for an CXL RCEC and pass it down to the connected
> > CXL device's error handler. With this approach the code can entirely
> > be implemented in the PCIe AER driver and is independent of the CXL
> > subsystem. The CXL driver only provides the handler.
> >
> > [1] CXL 3.0 spec, 12.2.1.1 RCH Downstream Port-detected Errors
> > [2] CXL 3.0 spec, 8.1.3 PCIe DVSEC for CXL Devices
> >
> > Co-developed-by: Terry Bowman <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> > Signed-off-by: Robert Richter <[email protected]>
> > Cc: "Oliver O'Halloran" <[email protected]>
> > Cc: Bjorn Helgaas <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
>
> Given the questions are minor:
Will update description according to you comment.
>
> Acked-by: Bjorn Helgaas <[email protected]>
Thanks for review and the ACK.
-Robert
>
> > ---
> > drivers/pci/pcie/Kconfig | 12 +++++
> > drivers/pci/pcie/aer.c | 100 ++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 110 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
> > index 228652a59f27..4f0e70fafe2d 100644
> > --- a/drivers/pci/pcie/Kconfig
> > +++ b/drivers/pci/pcie/Kconfig
> > @@ -49,6 +49,18 @@ config PCIEAER_INJECT
> > gotten from:
> > https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/
> >
> > +config PCIEAER_CXL
> > + bool "PCI Express CXL RAS support for Restricted Hosts (RCH)"
> > + default y
> > + depends on PCIEAER && CXL_PCI
> > + help
> > + Enables error handling of downstream ports of a CXL host
> > + that is operating in RCD mode (Restricted CXL Host, RCH).
> > + The downstream port reports AER errors to a given RCEC.
> > + Errors are handled by the CXL memory device driver.
> > +
> > + If unsure, say Y.
> > +
> > #
> > # PCI Express ECRC
> > #
> > diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> > index d3344fcf1f79..2e3f00b6a5bd 100644
> > --- a/drivers/pci/pcie/aer.c
> > +++ b/drivers/pci/pcie/aer.c
> > @@ -946,14 +946,104 @@ static bool find_source_device(struct pci_dev *parent,
> > return true;
> > }
> >
> > +#ifdef CONFIG_PCIEAER_CXL
> > +
> > +static bool is_cxl_mem_dev(struct pci_dev *dev)
> > +{
> > + /*
> > + * The capability, status, and control fields in Device 0,
> > + * Function 0 DVSEC control the CXL functionality of the
> > + * entire device (CXL 3.0, 8.1.3).
> > + */
> > + if (dev->devfn != PCI_DEVFN(0, 0))
> > + return false;
> > +
> > + /*
> > + * CXL Memory Devices must have the 502h class code set (CXL
> > + * 3.0, 8.1.12.1).
> > + */
> > + if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL)
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > +static bool cxl_error_is_native(struct pci_dev *dev)
> > +{
> > + struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
> > +
> > + if (pcie_ports_native)
> > + return true;
> > +
> > + return host->native_aer && host->native_cxl_error;
> > +}
> > +
> > +static bool is_internal_error(struct aer_err_info *info)
> > +{
> > + if (info->severity == AER_CORRECTABLE)
> > + return info->status & PCI_ERR_COR_INTERNAL;
> > +
> > + return info->status & PCI_ERR_UNC_INTN;
> > +}
> > +
> > +static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
> > +{
> > + struct aer_err_info *info = (struct aer_err_info *)data;
> > + const struct pci_error_handlers *err_handler;
> > +
> > + if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
> > + return 0;
> > +
> > + /* protect dev->driver */
> > + device_lock(&dev->dev);
> > +
> > + err_handler = dev->driver ? dev->driver->err_handler : NULL;
> > + if (!err_handler)
> > + goto out;
> > +
> > + if (info->severity == AER_CORRECTABLE) {
> > + if (err_handler->cor_error_detected)
> > + err_handler->cor_error_detected(dev);
> > + } else if (err_handler->error_detected) {
> > + if (info->severity == AER_NONFATAL)
> > + err_handler->error_detected(dev, pci_channel_io_normal);
> > + else if (info->severity == AER_FATAL)
> > + err_handler->error_detected(dev, pci_channel_io_frozen);
> > + }
> > +out:
> > + device_unlock(&dev->dev);
> > + return 0;
> > +}
> > +
> > +static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> > +{
> > + /*
> > + * CXL downstream ports of a CXL host that is operating in RCD
> > + * mode (RCH) signal errors as RCEC internal errors. Forward
> > + * them to all CXL devices below the RCEC.
> > + *
> > + * See CXL 3.0:
> > + * 9.11.8 CXL Devices Attached to an RCH
> > + * 12.2.1.1 RCH Downstream Port-detected Errors
> > + */
> > + if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
> > + is_internal_error(info))
> > + pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
> > +}
> > +
> > +#else
> > +static inline void cxl_rch_handle_error(struct pci_dev *dev,
> > + struct aer_err_info *info) { }
> > +#endif
> > +
> > /**
> > - * handle_error_source - handle logging error into an event log
> > + * pci_aer_handle_error - handle logging error into an event log
> > * @dev: pointer to pci_dev data structure of error source device
> > * @info: comprehensive error information
> > *
> > * Invoked when an error being detected by Root Port.
> > */
> > -static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> > +static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> > {
> > int aer = dev->aer_cap;
> >
> > @@ -977,6 +1067,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> > pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset);
> > else if (info->severity == AER_FATAL)
> > pcie_do_recovery(dev, pci_channel_io_frozen, aer_root_reset);
> > +}
> > +
> > +static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
> > +{
> > + cxl_rch_handle_error(dev, info);
> > + pci_aer_handle_error(dev, info);
> > pci_dev_put(dev);
> > }
> >
> > --
> > 2.34.1
> >
On 5/24/23 11:55, Bjorn Helgaas wrote:
> On Tue, May 23, 2023 at 06:22:09PM -0500, Terry Bowman wrote:
>> The CXL driver plans to use cper_print_aer() for logging restricted CXL
>> host (RCH) AER errors. cper_print_aer() is not currently exported and
>> therefore not usable by the CXL driver built as a loadable module. Export
>> the cper_print_aer() function making it available.
>>
>> The CONFIG_ACPI_APEI_PCIEAER kernel config is currently used to enable
>> cper_print_aer(). cper_print_aer() logs the AER registers and is
>> useful in PCIE AER logging outside of APEI. Remove the
>> CONFIG_ACPI_APEI_PCIEAER dependency to enable cper_print_aer().
>>
>> The cper_print_aer() function name implies CPER specific use but is useful
>> in non-CPER cases as well. Rename cper_print_aer() to pci_print_aer().
>>
>> Co-developed-by: Robert Richter <[email protected]>
>> Signed-off-by: Robert Richter <[email protected]>
>> Signed-off-by: Terry Bowman <[email protected]>
>> Cc: Mahesh J Salgaonkar <[email protected]>
>> Cc: "Oliver O'Halloran" <[email protected]>
>> Cc: Bjorn Helgaas <[email protected]>
>> Cc: [email protected]
>> Reviewed-by: Jonathan Cameron <[email protected]>
>
> Acked-by: Bjorn Helgaas <[email protected]>
>
Thank you Bjorn.
Regards,
Terry
On Thu, May 25, 2023 at 11:29:58PM +0200, Robert Richter wrote:
> eOn 24.05.23 16:32:35, Bjorn Helgaas wrote:
> > On Tue, May 23, 2023 at 06:22:13PM -0500, Terry Bowman wrote:
> > > From: Robert Richter <[email protected]>
> > >
> > > In Restricted CXL Device (RCD) mode a CXL device is exposed as an
> > > RCiEP, but CXL downstream and upstream ports are not enumerated and
> > > not visible in the PCIe hierarchy. Protocol and link errors are sent
> > > to an RCEC.
> > >
> > > Restricted CXL host (RCH) downstream port-detected errors are signaled
> > > as internal AER errors, either Uncorrectable Internal Error (UIE) or
> > > Corrected Internal Errors (CIE).
> >
> > From the parallelism with RCD above, I first thought that RCH devices
> > were non-RCD mode and *were* enumerated as part of the PCIe hierarchy,
> > but actually I suspect it's more like the following?
> >
> > ... but CXL downstream and upstream ports are not enumerated and not
> > visible in the PCIe hierarchy.
> >
> > Protocol and link errors from these non-enumerated ports are
> > signaled as internal AER errors ... via a CXL RCEC.
>
> Exactly, except the RCEC is standard PCIe and also must not
> necessarily on the same PCI bus as the CXL RCiEPs are.
So make it "RCEC" instead of "CXL RCEC", I guess? PCIe r6.0, sec
7.9.10.3, allows an RCEC to be associated with RCiEPs on different
buses, so nothing to see there.
> > > The error source is the id of the RCEC.
> >
> > This seems odd; I assume this refers to the RCEC's AER Error Source
> > Identification register, and the ERR_COR or ERR_FATAL/NONFATAL Source
> > Identification would ordinarily be the Requester ID of the RCiEP that
> > "sent" the Error Message. But you're saying it's actually the ID of
> > the *RCEC*, not the RCiEP?
>
> Right, the downstream port has its own AER ext capability in
> non-config (io mapped) RCRB register range. Errors originating from
> there are signaled as internal AER errors via the RCEC *with* the
> RCEC's Requester ID. Code walks through all associated CXL endpoints,
> determines the dport and checks its AER.
>
> There is also an RDPAS structure defined in CXL but that is only a
> different way to provide the RCEC to dport association instead of
> using the RCEC's Endpoint Association Extended Capability. In the end
> we get all associated RCHs and check the AER of all their dports.
>
> The upstream port is signaled using the RCiEP's AER. CXL spec is
> strict here: "Upstream Port RCRB shall not implement the AER Extended
> Capability." The RCiEP's requestor ID is used then and its config
> space the AER is in.
>
> CXL.cachemem errors are reported with the RCiEP as requester
> too. Status is in the CXL RAS cap and the UIE or CIE is set
> respectively in the AER status of the RCiEP.
>
> > We're going to call pci_aer_handle_error() as well, to handle the
> > non-internal errors, and I'm pretty sure that path expects the RCiEP
> > ID there.
> >
> > Whatever the answer, I'm not sure this sentence is actually relevant
> > to this patch, since this patch doesn't read PCI_ERR_ROOT_ERR_SRC or
> > look at struct aer_err_source.id.
>
> The source id is used in aer_process_err_devices() which finally calls
> handle_error_source() for the device with the requestor id. This is
> the place where cxl_rch_handle_error() checks if it is an RCEC that
> received an internal error and has cxl devices connected to it. Then,
> the request is forwarded to the cxl_mem handler which also needs to
> check the dport now. That is, pcie_walk_rcec() in
> cxl_rch_handle_error() is called with the RCEC's pci handle,
> cxl_rch_handle_error_iter() with the RCiEP's pci handle.
I'm still not sure this is relevant. Isn't that last sentence just
the way we always use pcie_walk_rcec()?
If there's something *different* here about CXL, and it's important to
this patch, sure. But I don't see that yet. Maybe a comment in the
code if you think it's important to clarify something there.
Bjorn
On 24.05.23 16:45:06, Bjorn Helgaas wrote:
> On Tue, May 23, 2023 at 06:22:14PM -0500, Terry Bowman wrote:
> > From: Robert Richter <[email protected]>
> >
> > AER corrected and uncorrectable internal errors (CIE/UIE) are masked
> > in their corresponding mask registers per default once in power-up
> > state. [1][2] Enable internal errors for RCECs to receive CXL
> > downstream port errors of Restricted CXL Hosts (RCHs).
> >
> > [1] CXL 3.0 Spec, 12.2.1.1 - RCH Downstream Port Detected Errors
> > [2] PCIe Base Spec 6.0, 7.8.4.3 Uncorrectable Error Mask Register,
> > 7.8.4.6 Correctable Error Mask Register
>
> I use "r6.0" to make sure it isn't mistaken for a section number.
>
> > Co-developed-by: Terry Bowman <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> > Signed-off-by: Robert Richter <[email protected]>
> > ---
> > drivers/pci/pcie/aer.c | 64 ++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 64 insertions(+)
> >
> > diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
> > index 2e3f00b6a5bd..c5076ae4eb58 100644
> > --- a/drivers/pci/pcie/aer.c
> > +++ b/drivers/pci/pcie/aer.c
> > @@ -948,6 +948,32 @@ static bool find_source_device(struct pci_dev *parent,
> >
> > #ifdef CONFIG_PCIEAER_CXL
> >
> > +static int pci_aer_unmask_internal_errors(struct pci_dev *dev)
> > +{
> > + int aer, rc;
> > + u32 mask;
> > +
> > + if (!pcie_aer_is_native(dev))
> > + return -EIO;
> > +
> > + aer = dev->aer_cap;
> > + rc = pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
> > + if (rc)
> > + return rc;
>
> I don't think there's much value in checking all these config accesses
> for failure. A failure return really just means you called it with
> invalid parameters; it doesn't tell you whether it was successful on
> PCI.
>
> > + mask &= ~PCI_ERR_UNC_INTN;
> > + rc = pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
> > + if (rc)
> > + return rc;
> > +
> > + rc = pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
> > + if (rc)
> > + return rc;
> > + mask &= ~PCI_ERR_COR_INTERNAL;
> > + rc = pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
> > +
> > + return rc;
> > +}
> > +
> > static bool is_cxl_mem_dev(struct pci_dev *dev)
> > {
> > /*
> > @@ -1031,7 +1057,44 @@ static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
> > pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
> > }
> >
> > +static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
> > +{
> > + int *handles_cxl = data;
> > +
> > + *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
>
> This effectively only looks at the *last* RCiEP associated with this
> RCEC. I would expect a logical OR of all of them.
>
> I see this is another use of is_cxl_mem_dev() and
> cxl_error_is_native() that really requires them to be in this file.
>
> > + return *handles_cxl;
If this is non-zero, the iteration stops. So as soon we find a cxl
device we can stop the loop. Else, all devices are non-cxl devs and
the last return is zero too.
Now checking the code, pci_walk_bus() works that way, but walk_rcec()
does not break in all cases. I think this function not working as
expected. We would need to check if pci_walk_bus() stopped the
iteration, e.g. with a return code.
Alternatively we could add this check:
if (!*handles_cxl)
*handles_cxl = ...
> > +}
> > +
> > +static bool handles_cxl_errors(struct pci_dev *rcec)
> > +{
> > + int handles_cxl = 0;
> > +
> > + if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
> > + pcie_aer_is_native(rcec))
> > + pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
> > +
> > + return !!handles_cxl;
> > +}
> > +
> > +static void cxl_rch_enable_rcec(struct pci_dev *rcec)
> > +{
> > + if (!handles_cxl_errors(rcec))
> > + return;
> > +
> > + /*
> > + * Internal errors are masked by default, unmask RCEC's here
> > + * PCI6.0 7.8.4.3 Uncorrectable Error Mask Register (Offset 08h)
> > + * PCI6.0 7.8.4.6 Correctable Error Mask Register (Offset 14h)
>
> The spec references seem superfluous here. The PCI_ERR_UNCOR_MASK and
> PCI_ERR_COR_MASK in pci_aer_unmask_internal_errors() are pretty good
> pointers.
>
> > + */
> > + if (pci_aer_unmask_internal_errors(rcec))
> > + pci_err(rcec, "CXL: Failed to unmask internal errors");
> > + else
> > + pci_info(rcec, "CXL: Internal errors unmasked");
> > +}
> > +
> > #else
> > +static inline void cxl_rch_enable_rcec(struct pci_dev *dev) { }
> > static inline void cxl_rch_handle_error(struct pci_dev *dev,
> > struct aer_err_info *info) { }
> > #endif
> > @@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
> > return status;
> > }
> >
> > + cxl_rch_enable_rcec(port);
>
> Could this be done by the driver that claims the CXL RCiEP? There's
> no point in unmasking the errors before there's a driver with
> pci_error_handlers that can do something with them anyway.
This sounds reasonable at the first glance. The problem is there could
be many devices associated with the RCEC. Not all of them will be
bound to a driver and handler at the same time. We would need to
refcount it or maintain a list of enabled devices. But there is
already something similar by checking dev->driver. But right, AER
errros could be seen and handled then at least on PCI level. I tent to
permanently enable RCEC AER, but that could cause side-effects. What
do you think?
Thanks,
-Robert
>
> > aer_enable_rootport(rpc);
> > pci_info(port, "enabled with IRQ %d\n", dev->irq);
> > return 0;
> > --
> > 2.34.1
> >
On 25.05.23 17:01:01, Bjorn Helgaas wrote:
> On Thu, May 25, 2023 at 11:29:58PM +0200, Robert Richter wrote:
> > eOn 24.05.23 16:32:35, Bjorn Helgaas wrote:
> > > On Tue, May 23, 2023 at 06:22:13PM -0500, Terry Bowman wrote:
> > > > From: Robert Richter <[email protected]>
> > > >
> > > > In Restricted CXL Device (RCD) mode a CXL device is exposed as an
> > > > RCiEP, but CXL downstream and upstream ports are not enumerated and
> > > > not visible in the PCIe hierarchy. Protocol and link errors are sent
> > > > to an RCEC.
> > > >
> > > > Restricted CXL host (RCH) downstream port-detected errors are signaled
> > > > as internal AER errors, either Uncorrectable Internal Error (UIE) or
> > > > Corrected Internal Errors (CIE).
> > >
> > > From the parallelism with RCD above, I first thought that RCH devices
> > > were non-RCD mode and *were* enumerated as part of the PCIe hierarchy,
> > > but actually I suspect it's more like the following?
> > >
> > > ... but CXL downstream and upstream ports are not enumerated and not
> > > visible in the PCIe hierarchy.
> > >
> > > Protocol and link errors from these non-enumerated ports are
> > > signaled as internal AER errors ... via a CXL RCEC.
> >
> > Exactly, except the RCEC is standard PCIe and also must not
> > necessarily on the same PCI bus as the CXL RCiEPs are.
>
> So make it "RCEC" instead of "CXL RCEC", I guess? PCIe r6.0, sec
> 7.9.10.3, allows an RCEC to be associated with RCiEPs on different
> buses, so nothing to see there.
Yes, nothing special. This makes it more difficult to check if the
RCEC has CXL devices connected, but still it is feasible.
>
> > > > The error source is the id of the RCEC.
> > >
> > > This seems odd; I assume this refers to the RCEC's AER Error Source
> > > Identification register, and the ERR_COR or ERR_FATAL/NONFATAL Source
> > > Identification would ordinarily be the Requester ID of the RCiEP that
> > > "sent" the Error Message. But you're saying it's actually the ID of
> > > the *RCEC*, not the RCiEP?
> >
> > Right, the downstream port has its own AER ext capability in
> > non-config (io mapped) RCRB register range. Errors originating from
> > there are signaled as internal AER errors via the RCEC *with* the
> > RCEC's Requester ID. Code walks through all associated CXL endpoints,
> > determines the dport and checks its AER.
> >
> > There is also an RDPAS structure defined in CXL but that is only a
> > different way to provide the RCEC to dport association instead of
> > using the RCEC's Endpoint Association Extended Capability. In the end
> > we get all associated RCHs and check the AER of all their dports.
> >
> > The upstream port is signaled using the RCiEP's AER. CXL spec is
> > strict here: "Upstream Port RCRB shall not implement the AER Extended
> > Capability." The RCiEP's requestor ID is used then and its config
> > space the AER is in.
> >
> > CXL.cachemem errors are reported with the RCiEP as requester
> > too. Status is in the CXL RAS cap and the UIE or CIE is set
> > respectively in the AER status of the RCiEP.
> >
> > > We're going to call pci_aer_handle_error() as well, to handle the
> > > non-internal errors, and I'm pretty sure that path expects the RCiEP
> > > ID there.
> > >
> > > Whatever the answer, I'm not sure this sentence is actually relevant
> > > to this patch, since this patch doesn't read PCI_ERR_ROOT_ERR_SRC or
> > > look at struct aer_err_source.id.
> >
> > The source id is used in aer_process_err_devices() which finally calls
> > handle_error_source() for the device with the requestor id. This is
> > the place where cxl_rch_handle_error() checks if it is an RCEC that
> > received an internal error and has cxl devices connected to it. Then,
> > the request is forwarded to the cxl_mem handler which also needs to
> > check the dport now. That is, pcie_walk_rcec() in
> > cxl_rch_handle_error() is called with the RCEC's pci handle,
> > cxl_rch_handle_error_iter() with the RCiEP's pci handle.
>
> I'm still not sure this is relevant. Isn't that last sentence just
> the way we always use pcie_walk_rcec()?
>
> If there's something *different* here about CXL, and it's important to
> this patch, sure. But I don't see that yet. Maybe a comment in the
> code if you think it's important to clarify something there.
The importance I see is that internal errors of an RCEC indicate an
AER error in an RCH's downstream port. Thus, once that happens, all
involved dports must be checked. Internal errors are typically
non-standard and implementation defined, but here it is CXL standard.
-Robert
On Fri, May 26, 2023 at 12:08:33AM +0200, Robert Richter wrote:
> On 24.05.23 16:45:06, Bjorn Helgaas wrote:
> > On Tue, May 23, 2023 at 06:22:14PM -0500, Terry Bowman wrote:
> > > From: Robert Richter <[email protected]>
> > >
> > > AER corrected and uncorrectable internal errors (CIE/UIE) are masked
> > > in their corresponding mask registers per default once in power-up
> > > state. [1][2] Enable internal errors for RCECs to receive CXL
> > > downstream port errors of Restricted CXL Hosts (RCHs).
> > > ...
> > > +static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
> > > +{
> > > + int *handles_cxl = data;
> > > +
> > > + *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
> >
> > This effectively only looks at the *last* RCiEP associated with this
> > RCEC. I would expect a logical OR of all of them.
> >
> > > + return *handles_cxl;
>
> If this is non-zero, the iteration stops. So as soon we find a cxl
> device we can stop the loop. Else, all devices are non-cxl devs and
> the last return is zero too.
>
> Now checking the code, pci_walk_bus() works that way, but walk_rcec()
> does not break in all cases. I think this function not working as
> expected. We would need to check if pci_walk_bus() stopped the
> iteration, e.g. with a return code.
>
> Alternatively we could add this check:
>
> if (!*handles_cxl)
> *handles_cxl = ...
If handles_cxl_error_iter() returns 1 (device is CXL mem, etc),
pci_walk_bus() will terminate. And handles_cxl_error_iter() also sets
*userdata to 1, so handles_cxl_errors() will return true.
I think that's all you need in this case: at least one associated
RCiEP might report errors you care about, so you should unmask RCEC
internal errors. You don't need to look at *all* the RCiEPs to know
that.
In the other case, cxl_rch_handle_error() does need to look at all the
RCiEPs, and cxl_rch_handle_error_iter() always returns 0, so it should
never terminate pci_walk_bus().
So I think I raised a false alarm here, and the current patches work
fine as-is. But I do think it's a little bit tricky to set
*handles_cxl and also use that as the return value and rely on it
terminating the loop. Maybe something like this would be more
straightforward?
static int handles_cxl_error_iter(...)
{
...
*handles_cxl |= is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
return 0;
}
Certainly not as efficient because it looks at more RCiEPs than
strictly necessary.
> > > +static bool handles_cxl_errors(struct pci_dev *rcec)
> > > +{
> > > + int handles_cxl = 0;
> > > +
> > > + if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
> > > + pcie_aer_is_native(rcec))
> > > + pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
> > > +
> > > + return !!handles_cxl;
> > > @@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
> > > return status;
> > > }
> > >
> > > + cxl_rch_enable_rcec(port);
> >
> > Could this be done by the driver that claims the CXL RCiEP? There's
> > no point in unmasking the errors before there's a driver with
> > pci_error_handlers that can do something with them anyway.
>
> This sounds reasonable at the first glance. The problem is there could
> be many devices associated with the RCEC. Not all of them will be
> bound to a driver and handler at the same time. We would need to
> refcount it or maintain a list of enabled devices. But there is
> already something similar by checking dev->driver. But right, AER
> errors could be seen and handled then at least on PCI level. I tent to
> permanently enable RCEC AER, but that could cause side-effects. What
> do you think?
IIUC, this really just affects CXL devices, so I think the choice is
(1) always unmask internal errors for RCECs where those CXL devices
report errors (as this patch does), or (2) unmask when first CXL
driver that can handle the errors is loaded and restore previous state
when last one is unloaded.
If the RCEC *only* handles errors for CXL devices, i.e., not for a mix
of vanilla PCIe RCiEPs and CXL RCiEPs, I think I'm OK with (1). I
think you said only the CXL driver knows how to collect and interpret
the error data. Is it OK that when no such driver is loaded, we field
error interrupts silently, without even mentioning that an error
occurred? I guess without the driver, the device is probably not in
use.
Bjorn
On Tue, 23 May 2023 18:21:52 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The RCRB is extracted already during ACPI CEDT table parsing while the
> data of this is needed not earlier than dport creation. This
> implementation comes with drawbacks: During ACPI table scan there is
> already MMIO access including mapping and unmapping, but only ACPI
> data should be collected here. The collected data must be transferred
> through a couple of interfaces until it is finally consumed when
> creating the dport. This causes complex data structures and function
> interfaces. Additionally, RCRB parsing will be extended to also
> extract AER data, it would be much easier do this at a later point
> during port and dport creation when the data structures are available
> to hold that data.
>
> To simplify all that, probe the RCRB at a later point during RCH
> downstream port creation. Change ACPI table parser to only extract the
> base address of either the component registers or the RCRB. Parse and
> extract the RCRB in devm_cxl_add_rch_dport().
>
> This is in preparation to centralize all RCRB scanning.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Hi,
Some comments inline, though one of them (about extensibility of CDAT
structures) applies just as much to the existing code so doesn't affect
Reviewed-by: Jonathan Cameron <[email protected]>
for this patch.
> ---
> drivers/cxl/acpi.c | 52 ++++++++++++++++-------------------------
> drivers/cxl/core/port.c | 21 +++++++++++++----
> drivers/cxl/cxl.h | 1 -
> 3 files changed, 36 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 7e1765b09e04..39227070da9b 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -373,20 +373,18 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> }
>
> struct cxl_chbs_context {
> - struct device *dev;
> unsigned long long uid;
> - resource_size_t rcrb;
> - resource_size_t chbcr;
> + resource_size_t base;
> u32 cxl_version;
> };
>
> -static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
> +static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> const unsigned long end)
> {
> struct cxl_chbs_context *ctx = arg;
> struct acpi_cedt_chbs *chbs;
>
> - if (ctx->chbcr)
> + if (ctx->base)
> return 0;
>
> chbs = (struct acpi_cedt_chbs *) header;
> @@ -395,23 +393,16 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
> return 0;
>
> ctx->cxl_version = chbs->cxl_version;
> - ctx->rcrb = CXL_RESOURCE_NONE;
> - ctx->chbcr = CXL_RESOURCE_NONE;
> + ctx->base = CXL_RESOURCE_NONE;
>
> if (!chbs->base)
> return 0;
>
> - if (chbs->cxl_version != ACPI_CEDT_CHBS_VERSION_CXL11) {
> - ctx->chbcr = chbs->base;
Trivial: This is a functional change and should be called out -
previously the base address was stashed even if the length test
fails, now it isn't. May make no difference because it was never used
if that's the case, but would be nice to still mention it in patch description.
Also, ACPI tables are designed to be extensible and I think that
applies to CDAT tables as well - so this code should not be
checking for a precise match, but rather that it is greater than
or equal to the size we will read from.
> + if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11 &&
> + chbs->length != CXL_RCRB_SIZE)
> return 0;
> - }
>
> - if (chbs->length != CXL_RCRB_SIZE)
> - return 0;
> -
> - ctx->rcrb = chbs->base;
> - ctx->chbcr = cxl_rcrb_to_component(ctx->dev, chbs->base,
> - CXL_RCRB_DOWNSTREAM);
> + ctx->base = chbs->base;
>
> return 0;
> }
On Tue, 23 May 2023 18:21:54 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Reading code like dport->dport does not immediately suggest that this
> points to the corresponding device structure of the dport. Rename
> struct member @dport to @dev.
>
> While at it, also rename @new argument of add_dport() to @dport. This
> better describes the variable as a dport (e.g. new->dport becomes to
> dport->dev).
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
LGTM. That naming confused me a few times as well.
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:21:56 -0500
Terry Bowman <[email protected]> wrote:
> The endpoint implements component register setup code. Refactor it for
> reuse with RCRB, downstream port, and upstream port setup.
>
> Move PCI specifics from cxl_setup_regs() into cxl_pci_setup_regs().
>
> Move cxl_setup_regs() into cxl/core/regs.c and export it. This also
> includes supporting static functions cxl_map_registerblock(),
> cxl_unmap_register_block() and cxl_probe_regs().
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:21:55 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The corresponding device of a register mapping is used for devm
> operations and logging. For operations with struct cxl_register_map
> the device needs to be kept track separately. To simpify the involved
> function interfaces, add @dev to cxl_register_map.
>
> While at it also reorder function arguments of cxl_map_device_regs()
> and cxl_map_component_regs() to have the object @cxl_register_map
> first.
>
> In a result a bunch of functions are available to be used with a
> @cxl_register_map object.
>
> This patch is in preparation of reworking the component register setup
> code.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> ---
> drivers/cxl/core/hdm.c | 4 ++--
> drivers/cxl/core/regs.c | 22 ++++++++++++---------
> drivers/cxl/cxl.h | 10 ++++++----
> drivers/cxl/pci.c | 42 ++++++++++++++++++++---------------------
> 4 files changed, 41 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 7889ff203a34..5abfa9276dac 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> struct cxl_component_regs *regs)
> {
> struct cxl_register_map map = {
> + .dev = &port->dev,
> .resource = port->component_reg_phys,
> .base = crb,
> .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
> @@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> return -ENODEV;
> }
>
> - return cxl_map_component_regs(&port->dev, regs, &map,
> - BIT(CXL_CM_CAP_CAP_ID_HDM));
> + return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
> }
>
> static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 08da4c917f99..9888bdf43e55 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -199,8 +199,9 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> return ret_val;
> }
>
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map, unsigned long map_mask)
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> + unsigned long map_mask)
> {
> struct mapinfo {
> struct cxl_reg_map *rmap;
> @@ -213,16 +214,16 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
>
> for (i = 0; i < ARRAY_SIZE(mapinfo); i++) {
> struct mapinfo *mi = &mapinfo[i];
> - resource_size_t phys_addr;
> + resource_size_t addr;
This rename not mentioned in the patch description. I guess it's
stepping towards consistency between the different paths, but
in this patch it looks like noise...
> resource_size_t length;
>
> if (!mi->rmap->valid)
> continue;
> if (!test_bit(mi->rmap->id, &map_mask))
> continue;
> - phys_addr = map->resource + mi->rmap->offset;
> + addr = map->resource + mi->rmap->offset;
> length = mi->rmap->size;
> - *(mi->addr) = devm_cxl_iomap_block(dev, phys_addr, length);
> + *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
> if (!*(mi->addr))
> return -ENOMEM;
> }
> @@ -231,9 +232,8 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
>
> -int cxl_map_device_regs(struct device *dev,
> - struct cxl_device_regs *regs,
> - struct cxl_register_map *map)
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs)
> {
> resource_size_t phys_addr = map->resource;
> struct mapinfo {
> @@ -256,7 +256,7 @@ int cxl_map_device_regs(struct device *dev,
>
> addr = phys_addr + mi->rmap->offset;
> length = mi->rmap->size;
> - *(mi->addr) = devm_cxl_iomap_block(dev, addr, length);
> + *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
> if (!*(mi->addr))
> return -ENOMEM;
> }
> @@ -302,7 +302,10 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> u32 regloc_size, regblocks;
> int regloc, i;
>
> + memset(map, 0, sizeof(*map));
> + map->dev = &pdev->dev;
> map->resource = CXL_RESOURCE_NONE;
> +
No comment :)
> regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
> CXL_DVSEC_REG_LOCATOR);
> if (!regloc)
> @@ -328,6 +331,7 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> }
>
> map->resource = CXL_RESOURCE_NONE;
> +
Not here either.
White space changes go in a patch on their own - not mixed in!
> return -ENODEV;
> }
> EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a8bda2c74a85..095b767c21e9 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -231,6 +231,7 @@ struct cxl_device_reg_map {
>
> /**
> * struct cxl_register_map - DVSEC harvested register block mapping parameters
> + * @dev: device for devm operations and logging
> * @base: virtual base of the register-block-BAR + @block_offset
> * @resource: physical resource base of the register block
> * @max_size: maximum mapping size to perform register search
> @@ -239,6 +240,7 @@ struct cxl_device_reg_map {
> * @device_map: cxl_reg_maps for device registers
> */
> struct cxl_register_map {
> + struct device *dev;
> void __iomem *base;
> resource_size_t resource;
> resource_size_t max_size;
> @@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> struct cxl_component_reg_map *map);
> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> struct cxl_device_reg_map *map);
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map,
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> unsigned long map_mask);
> -int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
> - struct cxl_register_map *map);
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs);
>
> enum cxl_regloc_type;
> int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 0872f2233ed0..2a9f65be148b 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,61 +274,59 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> -static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_map_regblock(struct cxl_register_map *map)
> {
> - struct device *dev = &pdev->dev;
It think equivalent is still a nice to have + smaller diff which is nice.
struct device *dev = &map->dev;
> -
> map->base = ioremap(map->resource, map->max_size);
> if (!map->base) {
> - dev_err(dev, "failed to map registers\n");
> + dev_err(map->dev, "failed to map registers\n");
> return -ENOMEM;
> }
>
> - dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
> + dev_dbg(map->dev, "Mapped CXL Memory Device resource %pa\n",
> + &map->resource);
> +
> return 0;
> }
>
> -static void cxl_unmap_regblock(struct pci_dev *pdev,
> - struct cxl_register_map *map)
> +static void cxl_unmap_regblock(struct cxl_register_map *map)
> {
> iounmap(map->base);
> map->base = NULL;
> }
>
> -static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_probe_regs(struct cxl_register_map *map)
> {
> struct cxl_component_reg_map *comp_map;
> struct cxl_device_reg_map *dev_map;
> - struct device *dev = &pdev->dev;
Same comment. Keep the local variable and this patch gets
smaller. I guess there might be a reason for this later in the
set though. If there is shout about it in the patch introduction.
> void __iomem *base = map->base;
>
> switch (map->reg_type) {
> case CXL_REGLOC_RBI_COMPONENT:
> comp_map = &map->component_map;
> - cxl_probe_component_regs(dev, base, comp_map);
> + cxl_probe_component_regs(map->dev, base, comp_map);
> if (!comp_map->hdm_decoder.valid) {
> - dev_err(dev, "HDM decoder registers not found\n");
> + dev_err(map->dev, "HDM decoder registers not found\n");
> return -ENXIO;
> }
>
> if (!comp_map->ras.valid)
> - dev_dbg(dev, "RAS registers not found\n");
> + dev_dbg(map->dev, "RAS registers not found\n");
>
> - dev_dbg(dev, "Set up component registers\n");
> + dev_dbg(map->dev, "Set up component registers\n");
> break;
> case CXL_REGLOC_RBI_MEMDEV:
> dev_map = &map->device_map;
> - cxl_probe_device_regs(dev, base, dev_map);
> + cxl_probe_device_regs(map->dev, base, dev_map);
> if (!dev_map->status.valid || !dev_map->mbox.valid ||
> !dev_map->memdev.valid) {
> - dev_err(dev, "registers not found: %s%s%s\n",
> + dev_err(map->dev, "registers not found: %s%s%s\n",
> !dev_map->status.valid ? "status " : "",
> !dev_map->mbox.valid ? "mbox " : "",
> !dev_map->memdev.valid ? "memdev " : "");
> return -ENXIO;
> }
>
> - dev_dbg(dev, "Probing device registers...\n");
> + dev_dbg(map->dev, "Probing device registers...\n");
> break;
> default:
> break;
> @@ -346,12 +344,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> if (rc)
> return rc;
>
> - rc = cxl_map_regblock(pdev, map);
> + rc = cxl_map_regblock(map);
> if (rc)
> return rc;
>
> - rc = cxl_probe_regs(pdev, map);
> - cxl_unmap_regblock(pdev, map);
> + rc = cxl_probe_regs(map);
> + cxl_unmap_regblock(map);
>
> return rc;
> }
> @@ -688,7 +686,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> return rc;
>
> - rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
> + rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
> if (rc)
> return rc;
>
> @@ -703,8 +701,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>
> cxlds->component_reg_phys = map.resource;
>
> - rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
> - &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
> + rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> + BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
> dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
>
On Tue, 23 May 2023 18:21:53 -0500
Terry Bowman <[email protected]> wrote:
> From: Dan Williams <[email protected]>
>
> Prepare cxl_probe_rcrb() for retrieving more than just the component
> register block. The RCH AER handling code wants to get back to the AER
> capability that happens to be MMIO mapped rather then configuration
> cycles.
>
> Move RCRB specific downstream port data, like the RCRB base and the
> AER capability offset, into its own data structure ('struct
> cxl_rcrb_info') for cxl_probe_rcrb() to fill. Extend 'struct
> cxl_dport' to include a 'struct cxl_rcrb_info' attribute.
>
There are several other refactors going on in here. I'd rather
see it broken down into a few separate patches. See inline.
> This centralizes all RCRB scanning in one routine.
>
> Signed-off-by: Dan Williams <[email protected]>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> ---
> drivers/cxl/core/port.c | 7 ++++---
> drivers/cxl/core/regs.c | 10 ++++++----
> drivers/cxl/cxl.h | 19 ++++++++++++-------
> drivers/cxl/mem.c | 16 +++++++++-------
> tools/testing/cxl/Kbuild | 2 +-
> tools/testing/cxl/test/cxl.c | 10 ++++++----
> tools/testing/cxl/test/mock.c | 12 ++++++------
> tools/testing/cxl/test/mock.h | 7 ++++---
> 8 files changed, 48 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 1a3f8729a616..618865ca6a9f 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -939,8 +939,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> return ERR_PTR(-ENOMEM);
>
> if (rcrb != CXL_RESOURCE_NONE) {
> - component_reg_phys = cxl_rcrb_to_component(dport_dev,
> - rcrb, CXL_RCRB_DOWNSTREAM);
> + component_reg_phys =
> + cxl_probe_rcrb(dport_dev, rcrb, &dport->rcrb,
> + CXL_RCRB_DOWNSTREAM);
> if (component_reg_phys == CXL_RESOURCE_NONE) {
> dev_warn(dport_dev, "Invalid Component Registers in RCRB");
> return ERR_PTR(-ENXIO);
> @@ -957,7 +958,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dport->port_id = port_id;
> dport->component_reg_phys = component_reg_phys;
> dport->port = port;
> - dport->rcrb = rcrb;
> + dport->rcrb.base = rcrb;
>
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 1476a0299c9b..08da4c917f99 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -332,9 +332,8 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
>
> -resource_size_t cxl_rcrb_to_component(struct device *dev,
> - resource_size_t rcrb,
> - enum cxl_rcrb which)
> +resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
> + struct cxl_rcrb_info *ri, enum cxl_rcrb which)
> {
> resource_size_t component_reg_phys;
> void __iomem *addr;
> @@ -344,6 +343,8 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
>
> if (which == CXL_RCRB_UPSTREAM)
> rcrb += SZ_4K;
> + else if (ri)
> + ri->base = rcrb;
I'm struggling a bit to follow flow, but I 'think' you set this to the same
address here and at the end of __devm_cxl_add_dport()
>
> /*
> * RCRB's BAR[0..1] point to component block containing CXL
> @@ -364,6 +365,7 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
> cmd = readw(addr + PCI_COMMAND);
> bar0 = readl(addr + PCI_BASE_ADDRESS_0);
> bar1 = readl(addr + PCI_BASE_ADDRESS_1);
> +
Trivial but I love to moan about these :)
Stray change that shouldn't be in this patch...
> iounmap(addr);
> release_mem_region(rcrb, SZ_4K);
>
> @@ -395,4 +397,4 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
>
> return component_reg_phys;
> }
> -EXPORT_SYMBOL_NS_GPL(cxl_rcrb_to_component, CXL);
> +EXPORT_SYMBOL_NS_GPL(cxl_probe_rcrb, CXL);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a5cd661face2..29e0bd2b8f2a 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -267,9 +267,9 @@ enum cxl_rcrb {
> CXL_RCRB_DOWNSTREAM,
> CXL_RCRB_UPSTREAM,
> };
> -resource_size_t cxl_rcrb_to_component(struct device *dev,
> - resource_size_t rcrb,
> - enum cxl_rcrb which);
> +struct cxl_rcrb_info;
> +resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
> + struct cxl_rcrb_info *ri, enum cxl_rcrb which);
>
> #define CXL_RESOURCE_NONE ((resource_size_t) -1)
> #define CXL_TARGET_STRLEN 20
> @@ -587,22 +587,27 @@ cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
> return xa_load(&port->dports, (unsigned long)dport_dev);
> }
>
> +struct cxl_rcrb_info {
> + resource_size_t base;
> + u16 aer_cap;
> +};
> +
> /**
> * struct cxl_dport - CXL downstream port
> * @dport: PCI bridge or firmware device representing the downstream link
> + * @port: reference to cxl_port that contains this downstream port
> * @port_id: unique hardware identifier for dport in decoder target list
> * @component_reg_phys: downstream port component registers
> - * @rcrb: base address for the Root Complex Register Block
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> - * @port: reference to cxl_port that contains this downstream port
> + * @rcrb: Data about the Root Complex Register Block layout
> */
> struct cxl_dport {
> struct device *dport;
> + struct cxl_port *port;
Why the reorder? It's adding noise we don't need in this patch...
> int port_id;
> resource_size_t component_reg_phys;
> - resource_size_t rcrb;
> bool rch;
> - struct cxl_port *port;
> + struct cxl_rcrb_info rcrb;
> };
>
> /**
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 519edd0eb196..7ecdaa7f9315 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> struct cxl_port *parent_port = parent_dport->port;
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_port *endpoint, *iter, *down;
> - resource_size_t component_reg_phys;
> int rc;
>
> /*
> @@ -71,12 +70,15 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> * host-bridge RCRB if they are not already mapped via the
> * typical register locator mechanism.
> */
> - if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
> - component_reg_phys = cxl_rcrb_to_component(
> - &cxlmd->dev, parent_dport->rcrb, CXL_RCRB_UPSTREAM);
> - else
> - component_reg_phys = cxlds->component_reg_phys;
> - endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
> + if (parent_dport->rch &&
> + cxlds->component_reg_phys == CXL_RESOURCE_NONE) {
> + cxlds->component_reg_phys =
> + cxl_probe_rcrb(&cxlmd->dev, parent_dport->rcrb.base,
> + NULL, CXL_RCRB_UPSTREAM);
This use of the component_reg_phys pointer in cxlds isn't closely related
to the other changes. This patch would (I think) be more readable
if that change was done in a precursor patch.
> + }
> +
> + endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> + cxlds->component_reg_phys,
> parent_dport);
> if (IS_ERR(endpoint))
> return PTR_ERR(endpoint);
On Tue, 23 May 2023 18:21:57 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Just moving code to reorder functions to later share cxl_get_chbs()
> with add_host_bridge_uport().
>
> This makes changes in the next patch visible. No other changes at all.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Given it's just a move FWIW
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/acpi.c | 90 +++++++++++++++++++++++-----------------------
> 1 file changed, 45 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 39227070da9b..4fd9fe32f830 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -327,51 +327,6 @@ __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> return NULL;
> }
>
> -/*
> - * A host bridge is a dport to a CFMWS decode and it is a uport to the
> - * dport (PCIe Root Ports) in the host bridge.
> - */
> -static int add_host_bridge_uport(struct device *match, void *arg)
> -{
> - struct cxl_port *root_port = arg;
> - struct device *host = root_port->dev.parent;
> - struct acpi_device *hb = to_cxl_host_bridge(host, match);
> - struct acpi_pci_root *pci_root;
> - struct cxl_dport *dport;
> - struct cxl_port *port;
> - struct device *bridge;
> - int rc;
> -
> - if (!hb)
> - return 0;
> -
> - pci_root = acpi_pci_find_root(hb->handle);
> - bridge = pci_root->bus->bridge;
> - dport = cxl_find_dport_by_dev(root_port, bridge);
> - if (!dport) {
> - dev_dbg(host, "host bridge expected and not found\n");
> - return 0;
> - }
> -
> - if (dport->rch) {
> - dev_info(bridge, "host supports CXL (restricted)\n");
> - return 0;
> - }
> -
> - rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> - if (rc)
> - return rc;
> -
> - port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> - dport);
> - if (IS_ERR(port))
> - return PTR_ERR(port);
> -
> - dev_info(bridge, "host supports CXL\n");
> -
> - return 0;
> -}
> -
> struct cxl_chbs_context {
> unsigned long long uid;
> resource_size_t base;
> @@ -464,6 +419,51 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> return 0;
> }
>
> +/*
> + * A host bridge is a dport to a CFMWS decode and it is a uport to the
> + * dport (PCIe Root Ports) in the host bridge.
> + */
> +static int add_host_bridge_uport(struct device *match, void *arg)
> +{
> + struct cxl_port *root_port = arg;
> + struct device *host = root_port->dev.parent;
> + struct acpi_device *hb = to_cxl_host_bridge(host, match);
> + struct acpi_pci_root *pci_root;
> + struct cxl_dport *dport;
> + struct cxl_port *port;
> + struct device *bridge;
> + int rc;
> +
> + if (!hb)
> + return 0;
> +
> + pci_root = acpi_pci_find_root(hb->handle);
> + bridge = pci_root->bus->bridge;
> + dport = cxl_find_dport_by_dev(root_port, bridge);
> + if (!dport) {
> + dev_dbg(host, "host bridge expected and not found\n");
> + return 0;
> + }
> +
> + if (dport->rch) {
> + dev_info(bridge, "host supports CXL (restricted)\n");
> + return 0;
> + }
> +
> + rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> + if (rc)
> + return rc;
> +
> + port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> + dport);
> + if (IS_ERR(port))
> + return PTR_ERR(port);
> +
> + dev_info(bridge, "host supports CXL\n");
> +
> + return 0;
> +}
> +
> static int add_root_nvdimm_bridge(struct device *match, void *data)
> {
> struct cxl_decoder *cxld;
On Tue, 23 May 2023 18:21:58 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> During a Host Bridge's downstream port enumeration the CHBS entries in
> the CEDT table are parsed, its Component Register base address
> extracted and then stored in struct cxl_dport. The CHBS may contain
> either the RCRB (RCH mode) or the Host Bridge's Component Registers
> (CHBCR, VH mode). The RCRB further contains the CXL downstream port
> register base address, while in VH mode the CXL Downstream Switch
> Ports are visible in the PCI hierarchy and the DP's component regs are
> disovered using the CXL DVSEC register locator capability. The
> Component Registers derived from the CHBS for both modes are different
> and thus also must be treated differently. That is, in RCH mode, the
> component regs base should be bound to the dport, but in VH mode to
> the CXL host bridge's port object.
>
> The current implementation stores the CHBCR in addition in struct
> cxl_dport and copies it later from there to struct cxl_port. As a
> result, the dport contains the wrong Component Registers base address
> and, e.g. the RAS capability of a CXL Root Port cannot be detected.
>
> To fix the CHBCR binding, attach it directly to the Host Bridge's
> @cxl_port structure. Do this during port creation of the Host Bridge
> in add_host_bridge_uport(). Factor out CHBS parsing code in
> add_host_bridge_dport() and use it in both functions.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
A few trivial formatting things. With those tidied up or
reason given for why not,
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/acpi.c | 65 +++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 50 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 4fd9fe32f830..78a24b2ca923 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -333,8 +333,8 @@ struct cxl_chbs_context {
> u32 cxl_version;
> };
>
> -static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> - const unsigned long end)
> +static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
> + const unsigned long end)
> {
> struct cxl_chbs_context *ctx = arg;
> struct acpi_cedt_chbs *chbs;
> @@ -362,6 +362,22 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> return 0;
> }
>
> +static int cxl_get_chbs(struct acpi_device *hb, struct cxl_chbs_context *ctx)
> +{
> + unsigned long long uid;
> + int rc;
> +
> + rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> + if (rc != AE_OK)
> + return -ENOENT;
> +
> + memset(ctx, 0, sizeof(*ctx));
> + ctx->uid = uid;
For consistency with original code better to use
*ctx = (struct cxl_chbs_context) {
.uid = uid,
};
> + acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
> +
> + return 0;
> +}
> +
> static int add_host_bridge_dport(struct device *match, void *arg)
> {
> acpi_status rc;
> @@ -377,19 +393,15 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> if (!hb)
> return 0;
>
> - rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> - if (rc != AE_OK) {
> + rc = cxl_get_chbs(hb, &ctx);
> + if (rc == -ENOENT)
> dev_err(match, "unable to retrieve _UID\n");
Why not push that down into the cxl_get_chbs() where no special handling
of error code is needed?
> - return -ENODEV;
> - }
> + if (rc)
> + return rc;
>
> + uid = ctx.uid;
> dev_dbg(match, "UID found: %lld\n", uid);
>
> - ctx = (struct cxl_chbs_context) {
> - .uid = uid,
> - };
> - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
> -
> if (!ctx.base) {
> dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
> uid);
> @@ -405,12 +417,17 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> pci_root = acpi_pci_find_root(hb->handle);
> bridge = pci_root->bus->bridge;
>
> + /*
> + * In RCH mode, bind the component regs base to the dport. In
> + * VH mode it will be bound to the CXL host bridge's port
> + * object later in add_host_bridge_uport().
> + */
> if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
> dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
> dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
> } else {
> - dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
> - dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
> + dport = devm_cxl_add_dport(root_port, bridge, uid,
> + CXL_RESOURCE_NONE);
> }
>
> if (IS_ERR(dport))
> @@ -432,6 +449,8 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> struct cxl_dport *dport;
> struct cxl_port *port;
> struct device *bridge;
> + struct cxl_chbs_context ctx;
> + resource_size_t component_reg_phys;
> int rc;
>
> if (!hb)
> @@ -450,12 +469,28 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> return 0;
> }
>
> + rc = cxl_get_chbs(hb, &ctx);
> + if (rc)
> + return rc;
> +
> + if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11)
> + /* RCH mode, should never happen */
> + return 0;
> +
> + if (ctx.base)
> + component_reg_phys = ctx.base;
> + else
> + component_reg_phys = CXL_RESOURCE_NONE;
> +
> + if (component_reg_phys != CXL_RESOURCE_NONE)
> + dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
> + ctx.uid, &component_reg_phys);
Why not put that in the block above? Fine leaving it here if this
makes sense after further refactoring.
> +
> rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> if (rc)
> return rc;
>
> - port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> - dport);
> + port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
> if (IS_ERR(port))
> return PTR_ERR(port);
>
On Tue, 23 May 2023 18:22:01 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> CXL capabilities are stored in the Component Registers. To use them,
> the specific I/O ranges of the capabilities must be determined by
> probing the registers. For this, the whole Component Register range
> needs to be mapped temporarily to detect the offset and length of a
> capability range.
>
> In order to use more than one capability of a component (e.g. RAS and
> HDM) the Component Register are probed and its mappings created
> multiple times. This also causes overlapping I/O ranges as the whole
> Component Register range must be mapped again while a capability's I/O
> range is already mapped.
>
> Different capabilities cannot be setup at the same time. E.g. the RAS
> capability must be made available as soon as the PCI driver is bound,
> the HDM decoder is setup later during port enumeration. Moreover,
> during early setup it is still unknown if a certain capability is
> needed. A central capability setup is therefore not possible,
> capabilities must be individually enabled once needed during
> initialization.
>
> To avoid a duplicate register probe and overlapping I/O mappings, only
> probe the Component Registers one time and store the Component
> Register mapping in struct port. The stored mappings can be used later
> to iomap the capability register range when enabling the capability,
> which will be implemented in a follow-on patch.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Some minor comments...
> ---
> drivers/cxl/core/port.c | 26 ++++++++++++++++++++++++++
> drivers/cxl/cxl.h | 2 ++
> 2 files changed, 28 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index eff91f141fde..34e929f1723b 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -686,6 +686,28 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> return ERR_PTR(rc);
> }
>
> +static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map,
> + resource_size_t component_reg_phys)
> +{
> + if (component_reg_phys == CXL_RESOURCE_NONE)
> + return -ENODEV;
> +
> + memset(map, 0, sizeof(*map));
> + map->dev = dev;
> + map->reg_type = CXL_REGLOC_RBI_COMPONENT;
> + map->resource = component_reg_phys;
> + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
setting most elements. Maybe
*map = (struct cxl_register_map) {
.dev = ...
etc is cleaner and effectively the same thing.
};
> +
> + return cxl_setup_regs(map);
> +}
> +
> +static inline int cxl_port_setup_regs(struct cxl_port *port,
> + resource_size_t component_reg_phys)
> +{
> + return cxl_setup_comp_regs(&port->dev, &port->comp_map,
> + component_reg_phys);
> +}
> +
> static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *uport,
> resource_size_t component_reg_phys,
> @@ -709,6 +731,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> if (rc)
> goto err;
>
> + rc = cxl_port_setup_regs(port, component_reg_phys);
> + if (rc && rc != -ENODEV)
> + goto err;
I'd add a comment on why not being present is fine here.
> +
> rc = device_add(dev);
> if (rc)
> goto err;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index e5ae5f4e6669..c76e1f84ba61 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -552,6 +552,7 @@ struct cxl_dax_region {
> * @regions: cxl_region_ref instances, regions mapped by this port
> * @parent_dport: dport that points to this port in the parent
> * @decoder_ida: allocator for decoder ids
> + * @comp_map: component register capability mappings
> * @nr_dports: number of entries in @dports
> * @hdm_end: track last allocated HDM decoder instance for allocation ordering
> * @commit_end: cursor to track highest committed decoder for commit ordering
> @@ -571,6 +572,7 @@ struct cxl_port {
> struct xarray regions;
> struct cxl_dport *parent_dport;
> struct ida decoder_ida;
> + struct cxl_register_map comp_map;
> int nr_dports;
> int hdm_end;
> int commit_end;
On Tue, 23 May 2023 18:21:59 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> When probing the Component Registers in function cxl_probe_regs()
> there are also checks for the existence of the HDM and RAS
> capabilities. The checks may fail for components that do not implement
> the HDM capability causing the Component Registers setup to fail too.
>
> Remove the checks for a generalized use of cxl_probe_regs() and check
> them directly before mapping the RAS or HDM capabilities. This allows
> it to setup other Component Registers esp. of an RCH Downstream Port,
> which will be implemented in a follow-on patch.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:22:02 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Same as for ports, also store the downstream port's Component Register
> mappings, use struct cxl_dport for that.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Seems reasonable to me.
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/core/port.c | 11 +++++++++++
> drivers/cxl/cxl.h | 2 ++
> 2 files changed, 13 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 34e929f1723b..db2ba0c886e2 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -708,6 +708,13 @@ static inline int cxl_port_setup_regs(struct cxl_port *port,
> component_reg_phys);
> }
>
> +static inline int cxl_dport_setup_regs(struct cxl_dport *dport,
> + resource_size_t component_reg_phys)
> +{
> + return cxl_setup_comp_regs(dport->dev, &dport->comp_map,
> + component_reg_phys);
> +}
> +
> static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *uport,
> resource_size_t component_reg_phys,
> @@ -986,6 +993,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dport->port = port;
> dport->rcrb.base = rcrb;
>
> + rc = cxl_dport_setup_regs(dport, component_reg_phys);
> + if (rc && rc != -ENODEV)
> + return ERR_PTR(rc);
> +
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> cond_cxl_root_unlock(port);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index c76e1f84ba61..dc83c1d0396e 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -601,6 +601,7 @@ struct cxl_rcrb_info {
> * struct cxl_dport - CXL downstream port
> * @dev: PCI bridge or firmware device representing the downstream link
> * @port: reference to cxl_port that contains this downstream port
> + * @comp_map: component register capability mappings
> * @port_id: unique hardware identifier for dport in decoder target list
> * @component_reg_phys: downstream port component registers
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> @@ -609,6 +610,7 @@ struct cxl_rcrb_info {
> struct cxl_dport {
> struct device *dev;
> struct cxl_port *port;
> + struct cxl_register_map comp_map;
> int port_id;
> resource_size_t component_reg_phys;
> bool rch;
On Tue, 23 May 2023 18:22:00 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> CXL RAS capabilities must be enabled and accessible as soon as the CXL
> endpoint is detected in the PCI hierarchy and bound to the cxl_pci
> driver. This needs to be independent of other modules such as cxl_port
> or cxl_mem.
>
> CXL RAS capabilities reside in the Component Registers. For an RCH
> this is determined by probing RCRB which is implemented very late once
> the CXL Memory Device is created.
>
> Change this by moving the RCRB probe to the cxl_pci driver. Do this by
> using a new introduced function cxl_pci_find_port() similar to
> cxl_mem_find_port() to determine the involved dport by the endpoint's
> PCI handle. Plug this into the existing cxl_pci_setup_regs() function
> to setup Component Registers. Probe the RCRB in case the Component
> Registers cannot be located through the CXL Register Locator
> capability.
>
> This unifies code and early sets up the Component Registers at the
> same time for both, VH and RCH mode. Only the cxl_pci driver is
> involved for this. This allows an early mapping of the CXL RAS
> capability registers.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
One minor wording suggestion inline. I'm don't really care
that much about it though, so.
Reviewed-by: Jonathan Cameron <[email protected]>
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 945ca0304d68..54c486cd65dd 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,13 +274,48 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> +/* Extract RCRB, use same function interface as cxl_find_regblock(). */
> +static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
> + enum cxl_regloc_type type,
> + struct cxl_register_map *map)
> +{
> + struct cxl_dport *dport;
> + resource_size_t component_reg_phys;
> +
> + memset(map, 0, sizeof(*map));
> + map->dev = &pdev->dev;
> + map->resource = CXL_RESOURCE_NONE;
> +
> + if (type != CXL_REGLOC_RBI_COMPONENT)
> + return -ENODEV;
> +
> + if (!cxl_pci_find_port(pdev, &dport) || !dport->rch)
> + return -ENXIO;
> +
> + component_reg_phys = cxl_probe_rcrb(&pdev->dev, dport->rcrb.base,
> + NULL, CXL_RCRB_UPSTREAM);
> + if (component_reg_phys == CXL_RESOURCE_NONE)
> + return -ENXIO;
> +
> + map->resource = component_reg_phys;
> + map->reg_type = type;
> + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
> +
> + return 0;
> +}
> +
> static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> struct cxl_register_map *map)
> {
> int rc;
>
> + /*
> + * If the Register Locator DVSEC does not contain the
> + * Component Registers, try to extract them from the RCRB if
> + * it is an RCH.
My instinct here was to wonder why having said 'if it is an RCH'
you don't seem to be checking that first. Perhaps
change this text to something like.
* Component Registers, assume it is an RCH and try to extra them
* from an RCRB.
*/
?
> + */
> rc = cxl_find_regblock(pdev, type, map);
> - if (rc)
> + if (rc && cxl_rcrb_get_comp_regs(pdev, type, map))
> return rc;
>
> return cxl_setup_regs(map);
On Tue, 23 May 2023 18:22:03 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Same as for ports and dports, also store the endpoint's Component
> Register mappings, use struct cxl_dev_state for that.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/cxlmem.h | 3 ++-
> drivers/cxl/pci.c | 9 +++++----
> 2 files changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index a2845a7a69d8..2823c5aaf3db 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -263,6 +263,7 @@ struct cxl_poison_state {
> *
> * @dev: The device associated with this CXL state
> * @cxlmd: The device representing the CXL.mem capabilities of @dev
> + * @comp_map: component register capability mappings
> * @regs: Parsed register blocks
> * @cxl_dvsec: Offset to the PCIe device DVSEC
> * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
> @@ -299,7 +300,7 @@ struct cxl_poison_state {
> struct cxl_dev_state {
> struct device *dev;
> struct cxl_memdev *cxlmd;
> -
> + struct cxl_register_map comp_map;
> struct cxl_regs regs;
> int cxl_dvsec;
>
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 54c486cd65dd..00983770ea7b 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -662,15 +662,16 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> * still be useful for management functions so don't return an error.
> */
> cxlds->component_reg_phys = CXL_RESOURCE_NONE;
> - rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
> + rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
> + &cxlds->comp_map);
> if (rc)
> dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
> - else if (!map.component_map.ras.valid)
> + else if (!cxlds->comp_map.component_map.ras.valid)
> dev_dbg(&pdev->dev, "RAS registers not found\n");
>
> - cxlds->component_reg_phys = map.resource;
> + cxlds->component_reg_phys = cxlds->comp_map.resource;
>
> - rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> + rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
> BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
> dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
On Tue, 23 May 2023 18:22:07 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/cxlmem.h | 2 --
> drivers/cxl/mem.c | 4 ++--
> drivers/cxl/pci.c | 3 ---
> 3 files changed, 2 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 2823c5aaf3db..7b5b2d3187bf 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -287,7 +287,6 @@ struct cxl_poison_state {
> * @active_persistent_bytes: sum of hard + soft persistent
> * @next_volatile_bytes: volatile capacity change pending device reset
> * @next_persistent_bytes: persistent capacity change pending device reset
> - * @component_reg_phys: register base of component registers
> * @info: Cached DVSEC information about the device.
> * @serial: PCIe Device Serial Number
> * @event: event log driver state
> @@ -326,7 +325,6 @@ struct cxl_dev_state {
> u64 next_volatile_bytes;
> u64 next_persistent_bytes;
>
> - resource_size_t component_reg_phys;
> u64 serial;
>
> struct cxl_event_state event;
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 0643852444f3..618e839919eb 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -49,7 +49,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> struct cxl_dport *parent_dport)
> {
> struct cxl_port *parent_port = parent_dport->port;
> - struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_port *endpoint, *iter, *down;
> int rc;
>
> @@ -65,8 +64,9 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> ep->next = down;
> }
>
> + /* The Endpoint's component regs are located in cxlds. */
> endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> - cxlds->component_reg_phys,
> + CXL_RESOURCE_NONE,
> parent_dport);
> if (IS_ERR(endpoint))
> return PTR_ERR(endpoint);
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 00983770ea7b..0db71493db5d 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -661,7 +661,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> * If the component registers can't be found, the cxl_pci driver may
> * still be useful for management functions so don't return an error.
> */
> - cxlds->component_reg_phys = CXL_RESOURCE_NONE;
> rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
> &cxlds->comp_map);
> if (rc)
> @@ -669,8 +668,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> else if (!cxlds->comp_map.component_map.ras.valid)
> dev_dbg(&pdev->dev, "RAS registers not found\n");
>
> - cxlds->component_reg_phys = cxlds->comp_map.resource;
> -
> rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
> BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
On Tue, 23 May 2023 18:22:06 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:22:05 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/core/port.c | 4 +---
> drivers/cxl/cxl.h | 2 --
> 2 files changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index db2ba0c886e2..183f9f8548e2 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -615,7 +615,6 @@ static int devm_cxl_link_parent_dport(struct device *host,
> static struct lock_class_key cxl_port_key;
>
> static struct cxl_port *cxl_port_alloc(struct device *uport,
> - resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport)
> {
> struct cxl_port *port;
> @@ -665,7 +664,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> } else
> dev->parent = uport;
>
> - port->component_reg_phys = component_reg_phys;
> ida_init(&port->decoder_ida);
> port->hdm_end = -1;
> port->commit_end = -1;
> @@ -724,7 +722,7 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *dev;
> int rc;
>
> - port = cxl_port_alloc(uport, component_reg_phys, parent_dport);
> + port = cxl_port_alloc(uport, parent_dport);
> if (IS_ERR(port))
> return port;
>
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index dc83c1d0396e..4365d46606df 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -556,7 +556,6 @@ struct cxl_dax_region {
> * @nr_dports: number of entries in @dports
> * @hdm_end: track last allocated HDM decoder instance for allocation ordering
> * @commit_end: cursor to track highest committed decoder for commit ordering
> - * @component_reg_phys: component register capability base address (optional)
> * @dead: last ep has been removed, force port re-creation
> * @depth: How deep this port is relative to the root. depth 0 is the root.
> * @cdat: Cached CDAT data
> @@ -576,7 +575,6 @@ struct cxl_port {
> int nr_dports;
> int hdm_end;
> int commit_end;
> - resource_size_t component_reg_phys;
> bool dead;
> unsigned int depth;
> struct cxl_cdat {
On Tue, 23 May 2023 18:22:04 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Now, that the Component Register mappings are stored, use them to
> enable and map the HDM decoder capabilities. The Component Registers
> do not need to be probed again for this, remove probing code.
>
> The HDM capability applies to Endpoints, USPs and VH Host Bridges. The
> Endpoint's component register mappings are located in the cxlds and
> else in the port's structure. Provide a helper function
> cxl_port_get_comp_map() to locate the mappings depending on the
> component's type.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Other than the static you already fixed this looks good to me.
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:22:08 -0500
Terry Bowman <[email protected]> wrote:
> Restricted CXL host (RCH) downstream port AER information is not currently
> logged while in the error state. One problem preventing the error logging
> is the AER and RAS registers are not accessible. The CXL driver requires
> changes to find RCH downstream port AER and RAS registers for purpose of
> error logging.
>
> RCH downstream ports are not enumerated during a PCI bus scan and are
> instead discovered using system firmware, ACPI in this case.[1] The
> downstream port is implemented as a Root Complex Register Block (RCRB).
> The RCRB is a 4k memory block containing PCIe registers based on the PCIe
> root port.[2] The RCRB includes AER extended capability registers used for
> reporting errors. Note, the RCH's AER Capability is located in the RCRB
> memory space instead of PCI configuration space, thus its register access
> is different. Existing kernel PCIe AER functions can not be used to manage
> the downstream port AER capabilities and RAS registers because the port was
> not enumerated during PCI scan and the registers are not PCI config
> accessible.
>
> Discover RCH downstream port AER extended capability registers. Use MMIO
> accesses to search for extended AER capability in RCRB register space.
>
> [1] CXL 3.0 Spec, 9.11.2 - System Firmware View of CXL 1.1 Hierarchy
> [2] CXL 3.0 Spec, 8.2.1.1 - RCH Downstream Port RCRB
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Seems reasonable.
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:22:10 -0500
Terry Bowman <[email protected]> wrote:
> The CXL error handler currently only logs endpoint RAS status. The CXL
> topology includes several components providing RAS details to be logged
> during error handling.[1] Update the current handler's RAS logging to use a
> RAS register address. This will allow for adding support to log other CXL
> component's RAS details in the future.
>
> [1] CXL3.0 Table 8-22 CXL_Capability_ID Assignment
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
On Tue, 23 May 2023 18:22:11 -0500
Terry Bowman <[email protected]> wrote:
The title is very vague. Can it be more specific to what
is in this patch? The description makes it seem like a bunch of
unconnected things, but in reality they all chain together
to get the registers and disable the interrupt.
> The restricted CXL host (RCH) error handler will log protocol errors
> using AER and RAS status registers. The AER and RAS registers need
> to be virtually memory mapped before enabling interrupts. Update
> __devm_cxl_add_dport() to include RCH RAS and AER mapping.
>
> Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
> the RCH downstream port's mapped registers.
>
> The RCH contains root command AER registers that should not be
> enabled.[1] Disable these to prevent root port interrupt generation.
>
> [1] CXL3.0 - 12.2.1.1 RCH Downstream Port-detected Errors
I just noticed this formatting of CXL3.0
It's CXL 3.0 or CXL rev 3.0 in most existing references in the tree so
good to keep to one of those instead of introducing another form
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> ---
> drivers/cxl/core/port.c | 64 +++++++++++++++++++++++++++++++++++++++++
> drivers/cxl/core/regs.c | 1 +
> drivers/cxl/cxl.h | 11 +++++++
> 3 files changed, 76 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index d147f08780d0..80c643254b86 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -8,6 +8,7 @@
> #include <linux/pci.h>
> #include <linux/slab.h>
> #include <linux/idr.h>
> +#include <linux/aer.h>
> #include <cxlmem.h>
> #include <cxlpci.h>
> #include <cxl.h>
> @@ -940,6 +941,63 @@ static void cxl_dport_unlink(void *data)
> sysfs_remove_link(&port->dev.kobj, link_name);
> }
>
> +static void cxl_disable_rch_root_ints(struct cxl_dport *dport)
> +{
> + void __iomem *aer_base = dport->regs.dport_aer;
> + u32 aer_cmd_mask, aer_cmd;
> +
> + if (!dport->rch || !aer_base)
> + return;
> +
> + /*
> + * Disable RCH root port command interrupts.
> + * CXL3.0 12.2.1.1 - RCH Downstream Port-detected Errors
Space after CXL?
> + *
> + * This sequnce may not be necessary. CXL spec states disabling
> + * the root cmd register's interrupts is required. But, PCI spec
> + * shows these are disabled by default on reset.
> + */
> + aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
> + PCI_ERR_ROOT_CMD_NONFATAL_EN |
> + PCI_ERR_ROOT_CMD_FATAL_EN);
> + aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
> + aer_cmd &= ~aer_cmd_mask;
> + writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
> +}
> +
> +static int cxl_dport_map_rch_aer(struct cxl_dport *dport)
> +{
> + struct cxl_rcrb_info *ri = &dport->rcrb;
> + resource_size_t aer_phys;
> + void __iomem *dport_aer;
> +
> + if (!dport->rch || !ri->aer_cap)
> + return -ENODEV;
> +
> + aer_phys = ri->aer_cap + ri->base;
> + dport_aer = devm_cxl_iomap_block(dport->dev, aer_phys,
> + sizeof(struct aer_capability_regs));
> + if (!dport_aer)
> + return -ENOMEM;
> +
> + dport->regs.dport_aer = dport_aer;
> +
> + return 0;
> +}
> +
> +static int cxl_dport_map_regs(struct cxl_dport *dport)
> +{
> + struct cxl_register_map *map = &dport->comp_map;
> +
> + if (!map->component_map.ras.valid)
> + dev_dbg(map->dev, "RAS registers not found\n");
> + else if (cxl_map_component_regs(map, &dport->regs.component,
> + BIT(CXL_CM_CAP_CAP_ID_RAS)))
> + dev_dbg(dport->dev, "Failed to map RAS capability.\n");
> +
> + return cxl_dport_map_rch_aer(dport);
> +}
> +
> static struct cxl_dport *
> __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> int port_id, resource_size_t component_reg_phys,
> @@ -994,6 +1052,12 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> if (rc && rc != -ENODEV)
> return ERR_PTR(rc);
>
> + rc = cxl_dport_map_regs(dport);
> + if (rc && rc != -ENODEV)
> + return ERR_PTR(rc);
> +
> + cxl_disable_rch_root_ints(dport);
> +
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> cond_cxl_root_unlock(port);
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 045abc11add8..b34f9e04cae4 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -198,6 +198,7 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
>
> return ret_val;
> }
> +EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);
>
> int cxl_map_component_regs(struct cxl_register_map *map,
> struct cxl_component_regs *regs,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 6134644b51f8..0e0bcbefefaf 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -209,6 +209,13 @@ struct cxl_regs {
> struct_group_tagged(cxl_device_regs, device_regs,
> void __iomem *status, *mbox, *memdev;
> );
> + /*
> + * RCH downstream port specific RAS register
> + * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
> + */
> + struct_group_tagged(cxl_rch_regs, rch_regs,
> + void __iomem *dport_aer;
> + );
> };
>
> struct cxl_reg_map {
> @@ -255,6 +262,8 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> struct cxl_component_reg_map *map);
> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> struct cxl_device_reg_map *map);
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> + resource_size_t length);
> int cxl_map_component_regs(struct cxl_register_map *map,
> struct cxl_component_regs *regs,
> unsigned long map_mask);
> @@ -603,6 +612,7 @@ struct cxl_rcrb_info {
> * @port_id: unique hardware identifier for dport in decoder target list
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> * @rcrb: Data about the Root Complex Register Block layout
> + * @regs: Dport parsed register blocks
> */
> struct cxl_dport {
> struct device *dev;
> @@ -611,6 +621,7 @@ struct cxl_dport {
> int port_id;
> bool rch;
> struct cxl_rcrb_info rcrb;
> + struct cxl_regs regs;
> };
>
> /**
On Tue, 23 May 2023 18:22:12 -0500
Terry Bowman <[email protected]> wrote:
> RCH downstream port error logging is missing in the current CXL driver. The
> missing AER and RAS error logging is needed for communicating driver error
> details to userspace. Update the driver to include PCIe AER and CXL RAS
> error logging.
>
> Add RCH downstream port error handling into the existing RCiEP handler.
> The downstream port error handler is added to the RCiEP error handler
> because the downstream port is implemented in a RCRB, is not PCI
> enumerable, and as a result is not directly accessible to the PCI AER
> root port driver. The AER root port driver calls the RCiEP handler for
> handling RCD errors and RCH downstream port protocol errors.
>
> Update existing RCiEP correctable and uncorrectable handlers to also call
> the RCH handler. The RCH handler will read the RCH AER registers, check for
> error severity, and if an error exists will log using an existing kernel
> AER trace routine. The RCH handler will also log downstream port RAS errors
> if they exist.
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
LGTM
Reviewed-by: Jonathan Cameron <[email protected]>
Hi Jonathan, thanks for reviewing.
On 6/1/23 08:49, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:22:11 -0500
> Terry Bowman <[email protected]> wrote:
>
> The title is very vague. Can it be more specific to what
> is in this patch? The description makes it seem like a bunch of
> unconnected things, but in reality they all chain together
> to get the registers and disable the interrupt.
>
How about I split this into 2 patches (along lines of register mapping
and root port interrupt disable) and title as:
cxl/pci: Map RCH downstream registers for AER protocol error logging
cxl/pci: Disable root port interrupts in RCH mode
>> The restricted CXL host (RCH) error handler will log protocol errors
>> using AER and RAS status registers. The AER and RAS registers need
>> to be virtually memory mapped before enabling interrupts. Update
>> __devm_cxl_add_dport() to include RCH RAS and AER mapping.
>>
>> Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
>> the RCH downstream port's mapped registers.
>>
>> The RCH contains root command AER registers that should not be
>> enabled.[1] Disable these to prevent root port interrupt generation.
>
>>
>> [1] CXL3.0 - 12.2.1.1 RCH Downstream Port-detected Errors
> I just noticed this formatting of CXL3.0
> It's CXL 3.0 or CXL rev 3.0 in most existing references in the tree so
> good to keep to one of those instead of introducing another form
>
Ok, I will change.
>>
>> Co-developed-by: Robert Richter <[email protected]>
>> Signed-off-by: Robert Richter <[email protected]>
>> Signed-off-by: Terry Bowman <[email protected]>
>> ---
>> drivers/cxl/core/port.c | 64 +++++++++++++++++++++++++++++++++++++++++
>> drivers/cxl/core/regs.c | 1 +
>> drivers/cxl/cxl.h | 11 +++++++
>> 3 files changed, 76 insertions(+)
>>
>> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
>> index d147f08780d0..80c643254b86 100644
>> --- a/drivers/cxl/core/port.c
>> +++ b/drivers/cxl/core/port.c
>> @@ -8,6 +8,7 @@
>> #include <linux/pci.h>
>> #include <linux/slab.h>
>> #include <linux/idr.h>
>> +#include <linux/aer.h>
>> #include <cxlmem.h>
>> #include <cxlpci.h>
>> #include <cxl.h>
>> @@ -940,6 +941,63 @@ static void cxl_dport_unlink(void *data)
>> sysfs_remove_link(&port->dev.kobj, link_name);
>> }
>>
>> +static void cxl_disable_rch_root_ints(struct cxl_dport *dport)
>> +{
>> + void __iomem *aer_base = dport->regs.dport_aer;
>> + u32 aer_cmd_mask, aer_cmd;
>> +
>> + if (!dport->rch || !aer_base)
>> + return;
>> +
>> + /*
>> + * Disable RCH root port command interrupts.
>> + * CXL3.0 12.2.1.1 - RCH Downstream Port-detected Errors
>
> Space after CXL?
>
Ok.
Regards,
Terry
>> + *
>> + * This sequnce may not be necessary. CXL spec states disabling
>> + * the root cmd register's interrupts is required. But, PCI spec
>> + * shows these are disabled by default on reset.
>> + */
>> + aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
>> + PCI_ERR_ROOT_CMD_NONFATAL_EN |
>> + PCI_ERR_ROOT_CMD_FATAL_EN);
>> + aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
>> + aer_cmd &= ~aer_cmd_mask;
>> + writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
>> +}
>> +
>> +static int cxl_dport_map_rch_aer(struct cxl_dport *dport)
>> +{
>> + struct cxl_rcrb_info *ri = &dport->rcrb;
>> + resource_size_t aer_phys;
>> + void __iomem *dport_aer;
>> +
>> + if (!dport->rch || !ri->aer_cap)
>> + return -ENODEV;
>> +
>> + aer_phys = ri->aer_cap + ri->base;
>> + dport_aer = devm_cxl_iomap_block(dport->dev, aer_phys,
>> + sizeof(struct aer_capability_regs));
>> + if (!dport_aer)
>> + return -ENOMEM;
>> +
>> + dport->regs.dport_aer = dport_aer;
>> +
>> + return 0;
>> +}
>> +
>> +static int cxl_dport_map_regs(struct cxl_dport *dport)
>> +{
>> + struct cxl_register_map *map = &dport->comp_map;
>> +
>> + if (!map->component_map.ras.valid)
>> + dev_dbg(map->dev, "RAS registers not found\n");
>> + else if (cxl_map_component_regs(map, &dport->regs.component,
>> + BIT(CXL_CM_CAP_CAP_ID_RAS)))
>> + dev_dbg(dport->dev, "Failed to map RAS capability.\n");
>> +
>> + return cxl_dport_map_rch_aer(dport);
>> +}
>> +
>> static struct cxl_dport *
>> __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
>> int port_id, resource_size_t component_reg_phys,
>> @@ -994,6 +1052,12 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
>> if (rc && rc != -ENODEV)
>> return ERR_PTR(rc);
>>
>> + rc = cxl_dport_map_regs(dport);
>> + if (rc && rc != -ENODEV)
>> + return ERR_PTR(rc);
>> +
>> + cxl_disable_rch_root_ints(dport);
>> +
>> cond_cxl_root_lock(port);
>> rc = add_dport(port, dport);
>> cond_cxl_root_unlock(port);
>> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
>> index 045abc11add8..b34f9e04cae4 100644
>> --- a/drivers/cxl/core/regs.c
>> +++ b/drivers/cxl/core/regs.c
>> @@ -198,6 +198,7 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
>>
>> return ret_val;
>> }
>> +EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);
>>
>> int cxl_map_component_regs(struct cxl_register_map *map,
>> struct cxl_component_regs *regs,
>> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
>> index 6134644b51f8..0e0bcbefefaf 100644
>> --- a/drivers/cxl/cxl.h
>> +++ b/drivers/cxl/cxl.h
>> @@ -209,6 +209,13 @@ struct cxl_regs {
>> struct_group_tagged(cxl_device_regs, device_regs,
>> void __iomem *status, *mbox, *memdev;
>> );
>> + /*
>> + * RCH downstream port specific RAS register
>> + * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
>> + */
>> + struct_group_tagged(cxl_rch_regs, rch_regs,
>> + void __iomem *dport_aer;
>> + );
>> };
>>
>> struct cxl_reg_map {
>> @@ -255,6 +262,8 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
>> struct cxl_component_reg_map *map);
>> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
>> struct cxl_device_reg_map *map);
>> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
>> + resource_size_t length);
>> int cxl_map_component_regs(struct cxl_register_map *map,
>> struct cxl_component_regs *regs,
>> unsigned long map_mask);
>> @@ -603,6 +612,7 @@ struct cxl_rcrb_info {
>> * @port_id: unique hardware identifier for dport in decoder target list
>> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
>> * @rcrb: Data about the Root Complex Register Block layout
>> + * @regs: Dport parsed register blocks
>> */
>> struct cxl_dport {
>> struct device *dev;
>> @@ -611,6 +621,7 @@ struct cxl_dport {
>> int port_id;
>> bool rch;
>> struct cxl_rcrb_info rcrb;
>> + struct cxl_regs regs;
>> };
>>
>> /**
>
> > > > @@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
> > > > return status;
> > > > }
> > > >
> > > > + cxl_rch_enable_rcec(port);
> > >
> > > Could this be done by the driver that claims the CXL RCiEP? There's
> > > no point in unmasking the errors before there's a driver with
> > > pci_error_handlers that can do something with them anyway.
> >
> > This sounds reasonable at the first glance. The problem is there could
> > be many devices associated with the RCEC. Not all of them will be
> > bound to a driver and handler at the same time. We would need to
> > refcount it or maintain a list of enabled devices. But there is
> > already something similar by checking dev->driver. But right, AER
> > errors could be seen and handled then at least on PCI level. I tent to
> > permanently enable RCEC AER, but that could cause side-effects. What
> > do you think?
>
> IIUC, this really just affects CXL devices, so I think the choice is
> (1) always unmask internal errors for RCECs where those CXL devices
> report errors (as this patch does), or (2) unmask when first CXL
> driver that can handle the errors is loaded and restore previous state
> when last one is unloaded.
>
> If the RCEC *only* handles errors for CXL devices, i.e., not for a mix
> of vanilla PCIe RCiEPs and CXL RCiEPs, I think I'm OK with (1). I
> think you said only the CXL driver knows how to collect and interpret
> the error data. Is it OK that when no such driver is loaded, we field
> error interrupts silently, without even mentioning that an error
> occurred? I guess without the driver, the device is probably not in
> use.
It might be in use. Firmware may well have set up the CXL device and
even have put the kernel image in that memory for example. OS first RAS
handling won't be up until the driver loads though. Would be a bit
odd to mix OS first handling with firmware setup. I'd expect firmware
first handling in that case, but I don't think anything stops the two
being mixed.
Jonathan
On Thu, 1 Jun 2023 09:06:23 -0500
Terry Bowman <[email protected]> wrote:
> Hi Jonathan, thanks for reviewing.
>
> On 6/1/23 08:49, Jonathan Cameron wrote:
> > On Tue, 23 May 2023 18:22:11 -0500
> > Terry Bowman <[email protected]> wrote:
> >
> > The title is very vague. Can it be more specific to what
> > is in this patch? The description makes it seem like a bunch of
> > unconnected things, but in reality they all chain together
> > to get the registers and disable the interrupt.
> >
>
> How about I split this into 2 patches (along lines of register mapping
> and root port interrupt disable) and title as:
> cxl/pci: Map RCH downstream registers for AER protocol error logging
> cxl/pci: Disable root port interrupts in RCH mode
Sounds good to me.
Jonathan
Hi Jonathan,
On 01.06.23 11:13:28, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:21:52 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Robert Richter <[email protected]>
> >
> > The RCRB is extracted already during ACPI CEDT table parsing while the
> > data of this is needed not earlier than dport creation. This
> > implementation comes with drawbacks: During ACPI table scan there is
> > already MMIO access including mapping and unmapping, but only ACPI
> > data should be collected here. The collected data must be transferred
> > through a couple of interfaces until it is finally consumed when
> > creating the dport. This causes complex data structures and function
> > interfaces. Additionally, RCRB parsing will be extended to also
> > extract AER data, it would be much easier do this at a later point
> > during port and dport creation when the data structures are available
> > to hold that data.
> >
> > To simplify all that, probe the RCRB at a later point during RCH
> > downstream port creation. Change ACPI table parser to only extract the
> > base address of either the component registers or the RCRB. Parse and
> > extract the RCRB in devm_cxl_add_rch_dport().
> >
> > This is in preparation to centralize all RCRB scanning.
> >
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
>
> Hi,
>
> Some comments inline, though one of them (about extensibility of CDAT
> structures) applies just as much to the existing code so doesn't affect
>
> Reviewed-by: Jonathan Cameron <[email protected]>
> for this patch.
thanks for review. See inline.
>
>
> > ---
> > drivers/cxl/acpi.c | 52 ++++++++++++++++-------------------------
> > drivers/cxl/core/port.c | 21 +++++++++++++----
> > drivers/cxl/cxl.h | 1 -
> > 3 files changed, 36 insertions(+), 38 deletions(-)
> >
> > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > index 7e1765b09e04..39227070da9b 100644
> > --- a/drivers/cxl/acpi.c
> > +++ b/drivers/cxl/acpi.c
> > @@ -373,20 +373,18 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> > }
> >
> > struct cxl_chbs_context {
> > - struct device *dev;
> > unsigned long long uid;
> > - resource_size_t rcrb;
> > - resource_size_t chbcr;
> > + resource_size_t base;
> > u32 cxl_version;
> > };
> >
> > -static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
> > +static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> > const unsigned long end)
> > {
> > struct cxl_chbs_context *ctx = arg;
> > struct acpi_cedt_chbs *chbs;
> >
> > - if (ctx->chbcr)
> > + if (ctx->base)
> > return 0;
> >
> > chbs = (struct acpi_cedt_chbs *) header;
> > @@ -395,23 +393,16 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
> > return 0;
> >
> > ctx->cxl_version = chbs->cxl_version;
> > - ctx->rcrb = CXL_RESOURCE_NONE;
> > - ctx->chbcr = CXL_RESOURCE_NONE;
> > + ctx->base = CXL_RESOURCE_NONE;
> >
> > if (!chbs->base)
> > return 0;
> >
> > - if (chbs->cxl_version != ACPI_CEDT_CHBS_VERSION_CXL11) {
> > - ctx->chbcr = chbs->base;
>
> Trivial: This is a functional change and should be called out -
> previously the base address was stashed even if the length test
> fails, now it isn't. May make no difference because it was never used
> if that's the case, but would be nice to still mention it in patch description.
The logic changed but the intention is to have the same checks as
before. The length check is in only for the CXL11 case and no check
for VH mode. This is implemented as before and no functional change,
note the check later below in the old code which was the CXL11-only
path.
>
> Also, ACPI tables are designed to be extensible and I think that
> applies to CDAT tables as well - so this code should not be
> checking for a precise match, but rather that it is greater than
> or equal to the size we will read from.
I don't think the spec will change here as this is limited to RCD mode
only. Other than e.g. capability register ranges this is a block size,
there is no intention to extend it.
>
>
> > + if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11 &&
> > + chbs->length != CXL_RCRB_SIZE)
> > return 0;
> > - }
>
> >
> > - if (chbs->length != CXL_RCRB_SIZE)
> > - return 0;
Note this check here.
-Robert
> > -
> > - ctx->rcrb = chbs->base;
> > - ctx->chbcr = cxl_rcrb_to_component(ctx->dev, chbs->base,
> > - CXL_RCRB_DOWNSTREAM);
> > + ctx->base = chbs->base;
> >
> > return 0;
> > }
>
On 01.06.23 11:38:11, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:21:53 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Dan Williams <[email protected]>
> >
> > Prepare cxl_probe_rcrb() for retrieving more than just the component
> > register block. The RCH AER handling code wants to get back to the AER
> > capability that happens to be MMIO mapped rather then configuration
> > cycles.
> >
> > Move RCRB specific downstream port data, like the RCRB base and the
> > AER capability offset, into its own data structure ('struct
> > cxl_rcrb_info') for cxl_probe_rcrb() to fill. Extend 'struct
> > cxl_dport' to include a 'struct cxl_rcrb_info' attribute.
> >
> There are several other refactors going on in here. I'd rather
> see it broken down into a few separate patches. See inline.
I didn't want to split Dan's patch here and just started with it as a
base.
>
> > This centralizes all RCRB scanning in one routine.
> >
> > Signed-off-by: Dan Williams <[email protected]>
> > Co-developed-by: Robert Richter <[email protected]>
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> > ---
> > drivers/cxl/core/port.c | 7 ++++---
> > drivers/cxl/core/regs.c | 10 ++++++----
> > drivers/cxl/cxl.h | 19 ++++++++++++-------
> > drivers/cxl/mem.c | 16 +++++++++-------
> > tools/testing/cxl/Kbuild | 2 +-
> > tools/testing/cxl/test/cxl.c | 10 ++++++----
> > tools/testing/cxl/test/mock.c | 12 ++++++------
> > tools/testing/cxl/test/mock.h | 7 ++++---
> > 8 files changed, 48 insertions(+), 35 deletions(-)
> >
> > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> > index 1a3f8729a616..618865ca6a9f 100644
> > --- a/drivers/cxl/core/port.c
> > +++ b/drivers/cxl/core/port.c
> > @@ -939,8 +939,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> > return ERR_PTR(-ENOMEM);
> >
> > if (rcrb != CXL_RESOURCE_NONE) {
> > - component_reg_phys = cxl_rcrb_to_component(dport_dev,
> > - rcrb, CXL_RCRB_DOWNSTREAM);
> > + component_reg_phys =
> > + cxl_probe_rcrb(dport_dev, rcrb, &dport->rcrb,
> > + CXL_RCRB_DOWNSTREAM);
> > if (component_reg_phys == CXL_RESOURCE_NONE) {
> > dev_warn(dport_dev, "Invalid Component Registers in RCRB");
> > return ERR_PTR(-ENXIO);
> > @@ -957,7 +958,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> > dport->port_id = port_id;
> > dport->component_reg_phys = component_reg_phys;
> > dport->port = port;
> > - dport->rcrb = rcrb;
> > + dport->rcrb.base = rcrb;
> >
> > cond_cxl_root_lock(port);
> > rc = add_dport(port, dport);
> > diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> > index 1476a0299c9b..08da4c917f99 100644
> > --- a/drivers/cxl/core/regs.c
> > +++ b/drivers/cxl/core/regs.c
> > @@ -332,9 +332,8 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> > }
> > EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
> >
> > -resource_size_t cxl_rcrb_to_component(struct device *dev,
> > - resource_size_t rcrb,
> > - enum cxl_rcrb which)
> > +resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
> > + struct cxl_rcrb_info *ri, enum cxl_rcrb which)
> > {
> > resource_size_t component_reg_phys;
> > void __iomem *addr;
> > @@ -344,6 +343,8 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
> >
> > if (which == CXL_RCRB_UPSTREAM)
> > rcrb += SZ_4K;
> > + else if (ri)
> > + ri->base = rcrb;
>
> I'm struggling a bit to follow flow, but I 'think' you set this to the same
> address here and at the end of __devm_cxl_add_dport()
Yes, that is a duplicate assignment, good catch.
>
> >
> > /*
> > * RCRB's BAR[0..1] point to component block containing CXL
> > @@ -364,6 +365,7 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
> > cmd = readw(addr + PCI_COMMAND);
> > bar0 = readl(addr + PCI_BASE_ADDRESS_0);
> > bar1 = readl(addr + PCI_BASE_ADDRESS_1);
> > +
>
> Trivial but I love to moan about these :)
> Stray change that shouldn't be in this patch...
I think it is ok to also add such trivial changes in a patch like
this. A separate patch for trivial things (to improve) like this would
just spam the patch queue and isn't it worth.
But, there are no other changes in that area, so just keep it as is
and simply drop the change.
>
> > iounmap(addr);
> > release_mem_region(rcrb, SZ_4K);
> >
> > @@ -395,4 +397,4 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
> >
> > return component_reg_phys;
> > }
> > -EXPORT_SYMBOL_NS_GPL(cxl_rcrb_to_component, CXL);
> > +EXPORT_SYMBOL_NS_GPL(cxl_probe_rcrb, CXL);
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index a5cd661face2..29e0bd2b8f2a 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -267,9 +267,9 @@ enum cxl_rcrb {
> > CXL_RCRB_DOWNSTREAM,
> > CXL_RCRB_UPSTREAM,
> > };
> > -resource_size_t cxl_rcrb_to_component(struct device *dev,
> > - resource_size_t rcrb,
> > - enum cxl_rcrb which);
> > +struct cxl_rcrb_info;
> > +resource_size_t cxl_probe_rcrb(struct device *dev, resource_size_t rcrb,
> > + struct cxl_rcrb_info *ri, enum cxl_rcrb which);
> >
> > #define CXL_RESOURCE_NONE ((resource_size_t) -1)
> > #define CXL_TARGET_STRLEN 20
> > @@ -587,22 +587,27 @@ cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
> > return xa_load(&port->dports, (unsigned long)dport_dev);
> > }
> >
> > +struct cxl_rcrb_info {
> > + resource_size_t base;
> > + u16 aer_cap;
> > +};
> > +
> > /**
> > * struct cxl_dport - CXL downstream port
> > * @dport: PCI bridge or firmware device representing the downstream link
> > + * @port: reference to cxl_port that contains this downstream port
> > * @port_id: unique hardware identifier for dport in decoder target list
> > * @component_reg_phys: downstream port component registers
> > - * @rcrb: base address for the Root Complex Register Block
> > * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> > - * @port: reference to cxl_port that contains this downstream port
> > + * @rcrb: Data about the Root Complex Register Block layout
> > */
> > struct cxl_dport {
> > struct device *dport;
> > + struct cxl_port *port;
>
> Why the reorder? It's adding noise we don't need in this patch...
There is some rework of the struct anyway. @port is essential for that
object as it reflects the hierarchy. Also, having 64 bit pointers in
the beginning improves padding of the struct. Not a big deal but good
reasons to change the order.
>
> > int port_id;
> > resource_size_t component_reg_phys;
> > - resource_size_t rcrb;
> > bool rch;
> > - struct cxl_port *port;
> > + struct cxl_rcrb_info rcrb;
> > };
> >
> > /**
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index 519edd0eb196..7ecdaa7f9315 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> > struct cxl_port *parent_port = parent_dport->port;
> > struct cxl_dev_state *cxlds = cxlmd->cxlds;
> > struct cxl_port *endpoint, *iter, *down;
> > - resource_size_t component_reg_phys;
> > int rc;
> >
> > /*
> > @@ -71,12 +70,15 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> > * host-bridge RCRB if they are not already mapped via the
> > * typical register locator mechanism.
> > */
> > - if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
> > - component_reg_phys = cxl_rcrb_to_component(
> > - &cxlmd->dev, parent_dport->rcrb, CXL_RCRB_UPSTREAM);
> > - else
> > - component_reg_phys = cxlds->component_reg_phys;
> > - endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
> > + if (parent_dport->rch &&
> > + cxlds->component_reg_phys == CXL_RESOURCE_NONE) {
> > + cxlds->component_reg_phys =
> > + cxl_probe_rcrb(&cxlmd->dev, parent_dport->rcrb.base,
> > + NULL, CXL_RCRB_UPSTREAM);
>
> This use of the component_reg_phys pointer in cxlds isn't closely related
> to the other changes. This patch would (I think) be more readable
> if that change was done in a precursor patch.
This is an intermediate change and removed later. I will check if the
local component_reg_phys var could be kept here until removal.
Thanks,
-Robert
>
> > + }
> > +
> > + endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> > + cxlds->component_reg_phys,
> > parent_dport);
> > if (IS_ERR(endpoint))
> > return PTR_ERR(endpoint);
>
On 01.06.23 11:49:30, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:21:55 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Robert Richter <[email protected]>
> >
> > The corresponding device of a register mapping is used for devm
> > operations and logging. For operations with struct cxl_register_map
> > the device needs to be kept track separately. To simpify the involved
> > function interfaces, add @dev to cxl_register_map.
> >
> > While at it also reorder function arguments of cxl_map_device_regs()
> > and cxl_map_component_regs() to have the object @cxl_register_map
> > first.
> >
> > In a result a bunch of functions are available to be used with a
> > @cxl_register_map object.
> >
> > This patch is in preparation of reworking the component register setup
> > code.
> >
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> > ---
> > drivers/cxl/core/hdm.c | 4 ++--
> > drivers/cxl/core/regs.c | 22 ++++++++++++---------
> > drivers/cxl/cxl.h | 10 ++++++----
> > drivers/cxl/pci.c | 42 ++++++++++++++++++++---------------------
> > 4 files changed, 41 insertions(+), 37 deletions(-)
> >
> > diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> > index 7889ff203a34..5abfa9276dac 100644
> > --- a/drivers/cxl/core/hdm.c
> > +++ b/drivers/cxl/core/hdm.c
> > @@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> > struct cxl_component_regs *regs)
> > {
> > struct cxl_register_map map = {
> > + .dev = &port->dev,
> > .resource = port->component_reg_phys,
> > .base = crb,
> > .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
> > @@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> > return -ENODEV;
> > }
> >
> > - return cxl_map_component_regs(&port->dev, regs, &map,
> > - BIT(CXL_CM_CAP_CAP_ID_HDM));
> > + return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
> > }
> >
> > static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> > diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> > index 08da4c917f99..9888bdf43e55 100644
> > --- a/drivers/cxl/core/regs.c
> > +++ b/drivers/cxl/core/regs.c
> > @@ -199,8 +199,9 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> > return ret_val;
> > }
> >
> > -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> > - struct cxl_register_map *map, unsigned long map_mask)
> > +int cxl_map_component_regs(struct cxl_register_map *map,
> > + struct cxl_component_regs *regs,
> > + unsigned long map_mask)
> > {
> > struct mapinfo {
> > struct cxl_reg_map *rmap;
> > @@ -213,16 +214,16 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> >
> > for (i = 0; i < ARRAY_SIZE(mapinfo); i++) {
> > struct mapinfo *mi = &mapinfo[i];
> > - resource_size_t phys_addr;
> > + resource_size_t addr;
>
> This rename not mentioned in the patch description. I guess it's
> stepping towards consistency between the different paths, but
> in this patch it looks like noise...
Haven't added a comment in the description as this is a local change
only and right, also for consistency. But the original intention was
to shorten variable length here to keep the 80 char size limit for the
line.
>
> > resource_size_t length;
> >
> > if (!mi->rmap->valid)
> > continue;
> > if (!test_bit(mi->rmap->id, &map_mask))
> > continue;
> > - phys_addr = map->resource + mi->rmap->offset;
> > + addr = map->resource + mi->rmap->offset;
> > length = mi->rmap->size;
> > - *(mi->addr) = devm_cxl_iomap_block(dev, phys_addr, length);
> > + *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
> > if (!*(mi->addr))
> > return -ENOMEM;
> > }
> > @@ -231,9 +232,8 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> > }
> > EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
> >
> > -int cxl_map_device_regs(struct device *dev,
> > - struct cxl_device_regs *regs,
> > - struct cxl_register_map *map)
> > +int cxl_map_device_regs(struct cxl_register_map *map,
> > + struct cxl_device_regs *regs)
> > {
> > resource_size_t phys_addr = map->resource;
> > struct mapinfo {
> > @@ -256,7 +256,7 @@ int cxl_map_device_regs(struct device *dev,
> >
> > addr = phys_addr + mi->rmap->offset;
> > length = mi->rmap->size;
> > - *(mi->addr) = devm_cxl_iomap_block(dev, addr, length);
> > + *(mi->addr) = devm_cxl_iomap_block(map->dev, addr, length);
> > if (!*(mi->addr))
> > return -ENOMEM;
> > }
> > @@ -302,7 +302,10 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> > u32 regloc_size, regblocks;
> > int regloc, i;
> >
> > + memset(map, 0, sizeof(*map));
> > + map->dev = &pdev->dev;
> > map->resource = CXL_RESOURCE_NONE;
> > +
>
> No comment :)
This logicaly groups the setup of @map in a code block.
>
> > regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
> > CXL_DVSEC_REG_LOCATOR);
> > if (!regloc)
> > @@ -328,6 +331,7 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> > }
> >
> > map->resource = CXL_RESOURCE_NONE;
> > +
> Not here either.
>
> White space changes go in a patch on their own - not mixed in!
IMO, once touching a function coding style changes can be part of the
whole change. Splitting this in small trivial patches a bit too much
as long as the patch is still readable.
>
> > return -ENODEV;
> > }
> > EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index a8bda2c74a85..095b767c21e9 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -231,6 +231,7 @@ struct cxl_device_reg_map {
> >
> > /**
> > * struct cxl_register_map - DVSEC harvested register block mapping parameters
> > + * @dev: device for devm operations and logging
> > * @base: virtual base of the register-block-BAR + @block_offset
> > * @resource: physical resource base of the register block
> > * @max_size: maximum mapping size to perform register search
> > @@ -239,6 +240,7 @@ struct cxl_device_reg_map {
> > * @device_map: cxl_reg_maps for device registers
> > */
> > struct cxl_register_map {
> > + struct device *dev;
> > void __iomem *base;
> > resource_size_t resource;
> > resource_size_t max_size;
> > @@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> > struct cxl_component_reg_map *map);
> > void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> > struct cxl_device_reg_map *map);
> > -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> > - struct cxl_register_map *map,
> > +int cxl_map_component_regs(struct cxl_register_map *map,
> > + struct cxl_component_regs *regs,
> > unsigned long map_mask);
> > -int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
> > - struct cxl_register_map *map);
> > +int cxl_map_device_regs(struct cxl_register_map *map,
> > + struct cxl_device_regs *regs);
> >
> > enum cxl_regloc_type;
> > int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 0872f2233ed0..2a9f65be148b 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -274,61 +274,59 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> > return 0;
> > }
> >
> > -static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> > +static int cxl_map_regblock(struct cxl_register_map *map)
> > {
> > - struct device *dev = &pdev->dev;
> It think equivalent is still a nice to have + smaller diff which is nice.
I see your point here. Will just reassign @dev.
>
> struct device *dev = &map->dev;
>
> > -
> > map->base = ioremap(map->resource, map->max_size);
> > if (!map->base) {
> > - dev_err(dev, "failed to map registers\n");
> > + dev_err(map->dev, "failed to map registers\n");
> > return -ENOMEM;
> > }
> >
> > - dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
> > + dev_dbg(map->dev, "Mapped CXL Memory Device resource %pa\n",
> > + &map->resource);
> > +
> > return 0;
> > }
> >
> > -static void cxl_unmap_regblock(struct pci_dev *pdev,
> > - struct cxl_register_map *map)
> > +static void cxl_unmap_regblock(struct cxl_register_map *map)
> > {
> > iounmap(map->base);
> > map->base = NULL;
> > }
> >
> > -static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
> > +static int cxl_probe_regs(struct cxl_register_map *map)
> > {
> > struct cxl_component_reg_map *comp_map;
> > struct cxl_device_reg_map *dev_map;
> > - struct device *dev = &pdev->dev;
>
> Same comment. Keep the local variable and this patch gets
> smaller. I guess there might be a reason for this later in the
> set though. If there is shout about it in the patch introduction.
I don't think the change is later needed. Let's see.
Thanks,
-Robert
>
> > void __iomem *base = map->base;
> >
> > switch (map->reg_type) {
> > case CXL_REGLOC_RBI_COMPONENT:
> > comp_map = &map->component_map;
> > - cxl_probe_component_regs(dev, base, comp_map);
> > + cxl_probe_component_regs(map->dev, base, comp_map);
> > if (!comp_map->hdm_decoder.valid) {
> > - dev_err(dev, "HDM decoder registers not found\n");
> > + dev_err(map->dev, "HDM decoder registers not found\n");
> > return -ENXIO;
> > }
> >
> > if (!comp_map->ras.valid)
> > - dev_dbg(dev, "RAS registers not found\n");
> > + dev_dbg(map->dev, "RAS registers not found\n");
> >
> > - dev_dbg(dev, "Set up component registers\n");
> > + dev_dbg(map->dev, "Set up component registers\n");
> > break;
> > case CXL_REGLOC_RBI_MEMDEV:
> > dev_map = &map->device_map;
> > - cxl_probe_device_regs(dev, base, dev_map);
> > + cxl_probe_device_regs(map->dev, base, dev_map);
> > if (!dev_map->status.valid || !dev_map->mbox.valid ||
> > !dev_map->memdev.valid) {
> > - dev_err(dev, "registers not found: %s%s%s\n",
> > + dev_err(map->dev, "registers not found: %s%s%s\n",
> > !dev_map->status.valid ? "status " : "",
> > !dev_map->mbox.valid ? "mbox " : "",
> > !dev_map->memdev.valid ? "memdev " : "");
> > return -ENXIO;
> > }
> >
> > - dev_dbg(dev, "Probing device registers...\n");
> > + dev_dbg(map->dev, "Probing device registers...\n");
> > break;
> > default:
> > break;
> > @@ -346,12 +344,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> > if (rc)
> > return rc;
> >
> > - rc = cxl_map_regblock(pdev, map);
> > + rc = cxl_map_regblock(map);
> > if (rc)
> > return rc;
> >
> > - rc = cxl_probe_regs(pdev, map);
> > - cxl_unmap_regblock(pdev, map);
> > + rc = cxl_probe_regs(map);
> > + cxl_unmap_regblock(map);
> >
> > return rc;
> > }
> > @@ -688,7 +686,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> > if (rc)
> > return rc;
> >
> > - rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
> > + rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
> > if (rc)
> > return rc;
> >
> > @@ -703,8 +701,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >
> > cxlds->component_reg_phys = map.resource;
> >
> > - rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
> > - &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
> > + rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> > + BIT(CXL_CM_CAP_CAP_ID_RAS));
> > if (rc)
> > dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
> >
>
On 01.06.23 13:45:57, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:21:58 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Robert Richter <[email protected]>
> >
> > During a Host Bridge's downstream port enumeration the CHBS entries in
> > the CEDT table are parsed, its Component Register base address
> > extracted and then stored in struct cxl_dport. The CHBS may contain
> > either the RCRB (RCH mode) or the Host Bridge's Component Registers
> > (CHBCR, VH mode). The RCRB further contains the CXL downstream port
> > register base address, while in VH mode the CXL Downstream Switch
> > Ports are visible in the PCI hierarchy and the DP's component regs are
> > disovered using the CXL DVSEC register locator capability. The
> > Component Registers derived from the CHBS for both modes are different
> > and thus also must be treated differently. That is, in RCH mode, the
> > component regs base should be bound to the dport, but in VH mode to
> > the CXL host bridge's port object.
> >
> > The current implementation stores the CHBCR in addition in struct
> > cxl_dport and copies it later from there to struct cxl_port. As a
> > result, the dport contains the wrong Component Registers base address
> > and, e.g. the RAS capability of a CXL Root Port cannot be detected.
> >
> > To fix the CHBCR binding, attach it directly to the Host Bridge's
> > @cxl_port structure. Do this during port creation of the Host Bridge
> > in add_host_bridge_uport(). Factor out CHBS parsing code in
> > add_host_bridge_dport() and use it in both functions.
> >
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> A few trivial formatting things. With those tidied up or
> reason given for why not,
>
> Reviewed-by: Jonathan Cameron <[email protected]>
>
> > ---
> > drivers/cxl/acpi.c | 65 +++++++++++++++++++++++++++++++++++-----------
> > 1 file changed, 50 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > index 4fd9fe32f830..78a24b2ca923 100644
> > --- a/drivers/cxl/acpi.c
> > +++ b/drivers/cxl/acpi.c
> > @@ -333,8 +333,8 @@ struct cxl_chbs_context {
> > u32 cxl_version;
> > };
> >
> > -static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> > - const unsigned long end)
> > +static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
> > + const unsigned long end)
> > {
> > struct cxl_chbs_context *ctx = arg;
> > struct acpi_cedt_chbs *chbs;
> > @@ -362,6 +362,22 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> > return 0;
> > }
> >
> > +static int cxl_get_chbs(struct acpi_device *hb, struct cxl_chbs_context *ctx)
> > +{
> > + unsigned long long uid;
> > + int rc;
> > +
> > + rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> > + if (rc != AE_OK)
> > + return -ENOENT;
> > +
> > + memset(ctx, 0, sizeof(*ctx));
> > + ctx->uid = uid;
>
> For consistency with original code better to use
>
> *ctx = (struct cxl_chbs_context) {
> .uid = uid,
> };
The memset() pattern is much more common in the kernel, better
readable (and writable :-)) and shorter. I have started using it for
all the changes. There are not too many left:
drivers/cxl/core/mbox.c: *mbox = (struct cxl_mbox_cmd) {
drivers/cxl/core/mbox.c: *mem_cmd = (struct cxl_mem_command) {
drivers/cxl/core/mbox.c: *mem_cmd = (struct cxl_mem_command) {
drivers/cxl/core/mbox.c: *payload = (struct cxl_mbox_clear_event_payload) {
drivers/cxl/core/regs.c: *map = (struct cxl_component_reg_map) { 0 };
drivers/cxl/core/regs.c: *map = (struct cxl_device_reg_map){ 0 };
drivers/cxl/pci.c: *policy = (struct cxl_event_interrupt_policy) {
drivers/cxl/pmem.c: *cmd = (struct nd_cmd_get_config_size) {
drivers/cxl/pmem.c: *set_lsa = (struct cxl_mbox_set_lsa) {
>
> > + acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
> > +
> > + return 0;
> > +}
> > +
> > static int add_host_bridge_dport(struct device *match, void *arg)
> > {
> > acpi_status rc;
> > @@ -377,19 +393,15 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> > if (!hb)
> > return 0;
> >
> > - rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> > - if (rc != AE_OK) {
> > + rc = cxl_get_chbs(hb, &ctx);
> > + if (rc == -ENOENT)
> > dev_err(match, "unable to retrieve _UID\n");
>
> Why not push that down into the cxl_get_chbs() where no special handling
> of error code is needed?
All messages are generated here in this function and not in
cxl_get_chbs() at a lower level. That allows to reuse it later in
add_host_bridge_uport() there messages are different or unnecessary.
You would also need to pass @dev down to cxl_get_chbs() which polutes
the function i/f.
>
> > - return -ENODEV;
> > - }
> > + if (rc)
> > + return rc;
> >
> > + uid = ctx.uid;
> > dev_dbg(match, "UID found: %lld\n", uid);
> >
> > - ctx = (struct cxl_chbs_context) {
> > - .uid = uid,
> > - };
> > - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
> > -
> > if (!ctx.base) {
> > dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
> > uid);
> > @@ -405,12 +417,17 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> > pci_root = acpi_pci_find_root(hb->handle);
> > bridge = pci_root->bus->bridge;
> >
> > + /*
> > + * In RCH mode, bind the component regs base to the dport. In
> > + * VH mode it will be bound to the CXL host bridge's port
> > + * object later in add_host_bridge_uport().
> > + */
> > if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
> > dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
> > dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
> > } else {
> > - dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
> > - dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
> > + dport = devm_cxl_add_dport(root_port, bridge, uid,
> > + CXL_RESOURCE_NONE);
> > }
> >
> > if (IS_ERR(dport))
> > @@ -432,6 +449,8 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> > struct cxl_dport *dport;
> > struct cxl_port *port;
> > struct device *bridge;
> > + struct cxl_chbs_context ctx;
> > + resource_size_t component_reg_phys;
> > int rc;
> >
> > if (!hb)
> > @@ -450,12 +469,28 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> > return 0;
> > }
> >
> > + rc = cxl_get_chbs(hb, &ctx);
> > + if (rc)
> > + return rc;
> > +
> > + if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11)
> > + /* RCH mode, should never happen */
> > + return 0;
> > +
> > + if (ctx.base)
> > + component_reg_phys = ctx.base;
> > + else
> > + component_reg_phys = CXL_RESOURCE_NONE;
> > +
> > + if (component_reg_phys != CXL_RESOURCE_NONE)
> > + dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
> > + ctx.uid, &component_reg_phys);
>
> Why not put that in the block above? Fine leaving it here if this
> makes sense after further refactoring.
ctx.base could be CXL_RESOURCE_NONE which we want to catch too.
I stumbled over that too then reviewing my own code, but I don't see
how this could be made more obvious. Maybe I will add a comment here.
Thanks,
-Robert
>
> > +
> > rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> > if (rc)
> > return rc;
> >
> > - port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> > - dport);
> > + port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
> > if (IS_ERR(port))
> > return PTR_ERR(port);
> >
>
On 01.06.23 14:06:03, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:22:01 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Robert Richter <[email protected]>
> >
> > CXL capabilities are stored in the Component Registers. To use them,
> > the specific I/O ranges of the capabilities must be determined by
> > probing the registers. For this, the whole Component Register range
> > needs to be mapped temporarily to detect the offset and length of a
> > capability range.
> >
> > In order to use more than one capability of a component (e.g. RAS and
> > HDM) the Component Register are probed and its mappings created
> > multiple times. This also causes overlapping I/O ranges as the whole
> > Component Register range must be mapped again while a capability's I/O
> > range is already mapped.
> >
> > Different capabilities cannot be setup at the same time. E.g. the RAS
> > capability must be made available as soon as the PCI driver is bound,
> > the HDM decoder is setup later during port enumeration. Moreover,
> > during early setup it is still unknown if a certain capability is
> > needed. A central capability setup is therefore not possible,
> > capabilities must be individually enabled once needed during
> > initialization.
> >
> > To avoid a duplicate register probe and overlapping I/O mappings, only
> > probe the Component Registers one time and store the Component
> > Register mapping in struct port. The stored mappings can be used later
> > to iomap the capability register range when enabling the capability,
> > which will be implemented in a follow-on patch.
> >
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
> Some minor comments...
>
>
> > ---
> > drivers/cxl/core/port.c | 26 ++++++++++++++++++++++++++
> > drivers/cxl/cxl.h | 2 ++
> > 2 files changed, 28 insertions(+)
> >
> > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> > index eff91f141fde..34e929f1723b 100644
> > --- a/drivers/cxl/core/port.c
> > +++ b/drivers/cxl/core/port.c
> > @@ -686,6 +686,28 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> > return ERR_PTR(rc);
> > }
> >
> > +static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map,
> > + resource_size_t component_reg_phys)
> > +{
> > + if (component_reg_phys == CXL_RESOURCE_NONE)
> > + return -ENODEV;
> > +
> > + memset(map, 0, sizeof(*map));
> > + map->dev = dev;
> > + map->reg_type = CXL_REGLOC_RBI_COMPONENT;
> > + map->resource = component_reg_phys;
> > + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
> setting most elements. Maybe
> *map = (struct cxl_register_map) {
> .dev = ...
> etc is cleaner and effectively the same thing.
> };
I really like the memset() pattern for this. See my comment in an
earlier mail for this.
> > +
> > + return cxl_setup_regs(map);
> > +}
> > +
> > +static inline int cxl_port_setup_regs(struct cxl_port *port,
> > + resource_size_t component_reg_phys)
> > +{
> > + return cxl_setup_comp_regs(&port->dev, &port->comp_map,
> > + component_reg_phys);
> > +}
> > +
> > static struct cxl_port *__devm_cxl_add_port(struct device *host,
> > struct device *uport,
> > resource_size_t component_reg_phys,
> > @@ -709,6 +731,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> > if (rc)
> > goto err;
> >
> > + rc = cxl_port_setup_regs(port, component_reg_phys);
> > + if (rc && rc != -ENODEV)
> > + goto err;
>
> I'd add a comment on why not being present is fine here.
Yes, will add that.
"Some components may no capablities or implementation is optional. So
do not fail here if no component register block exists and
component_reg_phys is unset. Instead run the check later when setting
up the capabilities."
Thanks,
-Robert
>
>
> > +
> > rc = device_add(dev);
> > if (rc)
> > goto err;
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index e5ae5f4e6669..c76e1f84ba61 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -552,6 +552,7 @@ struct cxl_dax_region {
> > * @regions: cxl_region_ref instances, regions mapped by this port
> > * @parent_dport: dport that points to this port in the parent
> > * @decoder_ida: allocator for decoder ids
> > + * @comp_map: component register capability mappings
> > * @nr_dports: number of entries in @dports
> > * @hdm_end: track last allocated HDM decoder instance for allocation ordering
> > * @commit_end: cursor to track highest committed decoder for commit ordering
> > @@ -571,6 +572,7 @@ struct cxl_port {
> > struct xarray regions;
> > struct cxl_dport *parent_dport;
> > struct ida decoder_ida;
> > + struct cxl_register_map comp_map;
> > int nr_dports;
> > int hdm_end;
> > int commit_end;
>
On 01.06.23 13:59:31, Jonathan Cameron wrote:
> On Tue, 23 May 2023 18:22:00 -0500
> Terry Bowman <[email protected]> wrote:
>
> > From: Robert Richter <[email protected]>
> >
> > CXL RAS capabilities must be enabled and accessible as soon as the CXL
> > endpoint is detected in the PCI hierarchy and bound to the cxl_pci
> > driver. This needs to be independent of other modules such as cxl_port
> > or cxl_mem.
> >
> > CXL RAS capabilities reside in the Component Registers. For an RCH
> > this is determined by probing RCRB which is implemented very late once
> > the CXL Memory Device is created.
> >
> > Change this by moving the RCRB probe to the cxl_pci driver. Do this by
> > using a new introduced function cxl_pci_find_port() similar to
> > cxl_mem_find_port() to determine the involved dport by the endpoint's
> > PCI handle. Plug this into the existing cxl_pci_setup_regs() function
> > to setup Component Registers. Probe the RCRB in case the Component
> > Registers cannot be located through the CXL Register Locator
> > capability.
> >
> > This unifies code and early sets up the Component Registers at the
> > same time for both, VH and RCH mode. Only the cxl_pci driver is
> > involved for this. This allows an early mapping of the CXL RAS
> > capability registers.
> >
> > Signed-off-by: Robert Richter <[email protected]>
> > Signed-off-by: Terry Bowman <[email protected]>
>
> One minor wording suggestion inline. I'm don't really care
> that much about it though, so.
>
> Reviewed-by: Jonathan Cameron <[email protected]>
>
>
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 945ca0304d68..54c486cd65dd 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -274,13 +274,48 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> > return 0;
> > }
> >
> > +/* Extract RCRB, use same function interface as cxl_find_regblock(). */
> > +static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
> > + enum cxl_regloc_type type,
> > + struct cxl_register_map *map)
> > +{
> > + struct cxl_dport *dport;
> > + resource_size_t component_reg_phys;
> > +
> > + memset(map, 0, sizeof(*map));
> > + map->dev = &pdev->dev;
> > + map->resource = CXL_RESOURCE_NONE;
> > +
> > + if (type != CXL_REGLOC_RBI_COMPONENT)
> > + return -ENODEV;
> > +
> > + if (!cxl_pci_find_port(pdev, &dport) || !dport->rch)
> > + return -ENXIO;
> > +
> > + component_reg_phys = cxl_probe_rcrb(&pdev->dev, dport->rcrb.base,
> > + NULL, CXL_RCRB_UPSTREAM);
> > + if (component_reg_phys == CXL_RESOURCE_NONE)
> > + return -ENXIO;
> > +
> > + map->resource = component_reg_phys;
> > + map->reg_type = type;
> > + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
> > +
> > + return 0;
> > +}
> > +
> > static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> > struct cxl_register_map *map)
> > {
> > int rc;
> >
> > + /*
> > + * If the Register Locator DVSEC does not contain the
> > + * Component Registers, try to extract them from the RCRB if
> > + * it is an RCH.
>
> My instinct here was to wonder why having said 'if it is an RCH'
> you don't seem to be checking that first. Perhaps
> change this text to something like.
> * Component Registers, assume it is an RCH and try to extra them
> * from an RCRB.
> */
> ?
Will change that.
Thanks for review,
-Robert
>
> > + */
> > rc = cxl_find_regblock(pdev, type, map);
> > - if (rc)
> > + if (rc && cxl_rcrb_get_comp_regs(pdev, type, map))
> > return rc;
> >
> > return cxl_setup_regs(map);
>
On 01.06.23 15:11:34, Jonathan Cameron wrote:
>
> > > > > @@ -1432,6 +1495,7 @@ static int aer_probe(struct pcie_device *dev)
> > > > > return status;
> > > > > }
> > > > >
> > > > > + cxl_rch_enable_rcec(port);
> > > >
> > > > Could this be done by the driver that claims the CXL RCiEP? There's
> > > > no point in unmasking the errors before there's a driver with
> > > > pci_error_handlers that can do something with them anyway.
> > >
> > > This sounds reasonable at the first glance. The problem is there could
> > > be many devices associated with the RCEC. Not all of them will be
> > > bound to a driver and handler at the same time. We would need to
> > > refcount it or maintain a list of enabled devices. But there is
> > > already something similar by checking dev->driver. But right, AER
> > > errors could be seen and handled then at least on PCI level. I tent to
> > > permanently enable RCEC AER, but that could cause side-effects. What
> > > do you think?
> >
> > IIUC, this really just affects CXL devices, so I think the choice is
> > (1) always unmask internal errors for RCECs where those CXL devices
> > report errors (as this patch does), or (2) unmask when first CXL
> > driver that can handle the errors is loaded and restore previous state
> > when last one is unloaded.
> >
> > If the RCEC *only* handles errors for CXL devices, i.e., not for a mix
> > of vanilla PCIe RCiEPs and CXL RCiEPs, I think I'm OK with (1). I
> > think you said only the CXL driver knows how to collect and interpret
> > the error data. Is it OK that when no such driver is loaded, we field
> > error interrupts silently, without even mentioning that an error
> > occurred? I guess without the driver, the device is probably not in
> > use.
>
> It might be in use. Firmware may well have set up the CXL device and
> even have put the kernel image in that memory for example. OS first RAS
> handling won't be up until the driver loads though. Would be a bit
> odd to mix OS first handling with firmware setup. I'd expect firmware
> first handling in that case, but I don't think anything stops the two
> being mixed.
Right, CXL memory may have been set up by firmware. We will only see
AER errors (for the unmasked error types) then without further CXL
handling, which is IMO OK.
This all assumes a non-CXL aware system can clear the error status by
only using PCIe AER. That is, a CXL RAS error may not trigger again
(or at all) by only clearing the AER status and not the CXL RAS status
in the capability. I don't know what the spec says here and how
devices actually operate.
Maybe option (2) is easy to implement with the refcount_t API. So with
the first device probed we just enable the RCEC's internal errors and
disable them when the last device is removed. I think CXL RAS errors
will not be triggered then as internal error must be enabled for this,
either in the RCEC or the endpoint. Since internal errors must be
unmasked first which can only be done by the CXL driver, CXL RAS error
wont trigger an AER error message.
Thanks,
-Robert
On Tue, 23 May 2023 18:22:08 -0500
Terry Bowman <[email protected]> wrote:
> Restricted CXL host (RCH) downstream port AER information is not currently
> logged while in the error state. One problem preventing the error logging
> is the AER and RAS registers are not accessible. The CXL driver requires
> changes to find RCH downstream port AER and RAS registers for purpose of
> error logging.
>
> RCH downstream ports are not enumerated during a PCI bus scan and are
> instead discovered using system firmware, ACPI in this case.[1] The
> downstream port is implemented as a Root Complex Register Block (RCRB).
> The RCRB is a 4k memory block containing PCIe registers based on the PCIe
> root port.[2] The RCRB includes AER extended capability registers used for
> reporting errors. Note, the RCH's AER Capability is located in the RCRB
> memory space instead of PCI configuration space, thus its register access
> is different. Existing kernel PCIe AER functions can not be used to manage
> the downstream port AER capabilities and RAS registers because the port was
> not enumerated during PCI scan and the registers are not PCI config
> accessible.
>
> Discover RCH downstream port AER extended capability registers. Use MMIO
> accesses to search for extended AER capability in RCRB register space.
>
> [1] CXL 3.0 Spec, 9.11.2 - System Firmware View of CXL 1.1 Hierarchy
> [2] CXL 3.0 Spec, 8.2.1.1 - RCH Downstream Port RCRB
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>