2021-06-17 02:45:17

by Alison Schofield

[permalink] [raw]
Subject: [PATCH v3 0/2] CXL ACPI tables for object creation

Jonathan - I updated Patch 2 so I didn't keep your Reviewed-by tag.

Changes since v2 [1]:
- Warn and continue, rather than error out, on these acpi table parsing issues:
table length mismatch for either CHBS or CFMWS; duplicate uid's for CHBS.
(Ben, Jonathan)
- Update flow in cxl_acpi_match_chbs() (Ben, Jonathan)
- Improve naming cedt_table->acpi_cedt, cedt_base->cedt_subtable (Ben)
- Emit debug message only if CFMWS is greater than its expected length (Ben)
- Update the dev_err messages wrt the CFMWS expected length failure.
- Remove blank line before error handling block (Jonathan)
- Rebase to the CXL pending branch [2]

[1]: https://lore.kernel.org/linux-cxl/[email protected]/
[2]: https://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl.git/log/?h=pending

--
Parse the ACPI CXL Early Discovery Table (CEDT) and use the CHBS & CFMWS
when creating port and decoder objects.

CHBS: CXL Host Bridge Structure - Patch 1
CFMWS: CXL Fixed Memory Window Structure - Patch 2

Alison Schofield (2):
cxl/acpi: Add the Host Bridge base address to CXL port objects
cxl/acpi: Use the ACPI CFMWS to create static decoder objects

drivers/cxl/acpi.c | 217 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 212 insertions(+), 5 deletions(-)


base-commit: 21083f51521fb0f60dbac591f175c3ed48435af4
--
2.26.2


2021-06-17 02:45:42

by Alison Schofield

[permalink] [raw]
Subject: [PATCH v3 1/2] cxl/acpi: Add the Host Bridge base address to CXL port objects

The base address for the Host Bridge port component registers is located
in the CXL Host Bridge Structure (CHBS) of the ACPI CXL Early Discovery
Table (CEDT). Retrieve the CHBS for each Host Bridge (ACPI0016 device)
and include that base address in the port object.

Co-developed-by: Vishal Verma <[email protected]>
Signed-off-by: Vishal Verma <[email protected]>
Signed-off-by: Alison Schofield <[email protected]>
---
drivers/cxl/acpi.c | 98 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 93 insertions(+), 5 deletions(-)

diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 8a723f7f3f73..852b5c270464 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -8,6 +8,58 @@
#include <linux/pci.h>
#include "cxl.h"

+static struct acpi_table_header *acpi_cedt;
+
+static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
+{
+ struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
+ acpi_size len, cur = 0;
+ void *cedt_subtable;
+
+ len = acpi_cedt->length - sizeof(*acpi_cedt);
+ cedt_subtable = acpi_cedt + 1;
+
+ while (cur < len) {
+ struct acpi_cedt_header *c = cedt_subtable + cur;
+
+ if (c->type != ACPI_CEDT_TYPE_CHBS) {
+ cur += c->length;
+ continue;
+ }
+
+ chbs = cedt_subtable + cur;
+
+ if (dev_WARN_ONCE(dev, chbs->header.length < sizeof(*chbs),
+ "CHBS entry skipped: invalid length:%u\n",
+ chbs->header.length)) {
+ cur += c->length;
+ continue;
+ }
+
+ if (chbs->uid != uid) {
+ cur += c->length;
+ continue;
+ }
+
+ if (dev_WARN_ONCE(dev, chbs_match,
+ "CHBS entry skipped: duplicate UID:%u\n",
+ uid)) {
+ cur += c->length;
+ continue;
+ }
+
+ chbs_match = chbs;
+ cur += c->length;
+ }
+
+ return chbs_match ? chbs_match : ERR_PTR(-ENODEV);
+}
+
+static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
+{
+ return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
+}
+
struct cxl_walk_context {
struct device *dev;
struct pci_bus *root;
@@ -50,6 +102,21 @@ static int match_add_root_ports(struct pci_dev *pdev, void *data)
return 0;
}

+static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device *dev)
+{
+ struct cxl_dport *dport;
+
+ device_lock(&port->dev);
+ list_for_each_entry(dport, &port->dports, list)
+ if (dport->dport == dev) {
+ device_unlock(&port->dev);
+ return dport;
+ }
+
+ device_unlock(&port->dev);
+ return NULL;
+}
+
static struct acpi_device *to_cxl_host_bridge(struct device *dev)
{
struct acpi_device *adev = to_acpi_device(dev);
@@ -71,6 +138,7 @@ static int add_host_bridge_uport(struct device *match, void *arg)
struct acpi_pci_root *pci_root;
struct cxl_walk_context ctx;
struct cxl_decoder *cxld;
+ struct cxl_dport *dport;
struct cxl_port *port;

if (!bridge)
@@ -80,8 +148,14 @@ static int add_host_bridge_uport(struct device *match, void *arg)
if (!pci_root)
return -ENXIO;

- /* TODO: fold in CEDT.CHBS retrieval */
- port = devm_cxl_add_port(host, match, CXL_RESOURCE_NONE, root_port);
+ dport = find_dport_by_dev(root_port, match);
+ if (!dport) {
+ dev_dbg(host, "host bridge expected and not found\n");
+ return -ENODEV;
+ }
+
+ port = devm_cxl_add_port(host, match, dport->component_reg_phys,
+ root_port);
if (IS_ERR(port))
return PTR_ERR(port);
dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
@@ -120,6 +194,7 @@ static int add_host_bridge_dport(struct device *match, void *arg)
int rc;
acpi_status status;
unsigned long long uid;
+ struct acpi_cedt_chbs *chbs;
struct cxl_port *root_port = arg;
struct device *host = root_port->dev.parent;
struct acpi_device *bridge = to_cxl_host_bridge(match);
@@ -135,7 +210,12 @@ static int add_host_bridge_dport(struct device *match, void *arg)
return -ENODEV;
}

- rc = cxl_add_dport(root_port, match, uid, CXL_RESOURCE_NONE);
+ chbs = cxl_acpi_match_chbs(host, uid);
+ if (IS_ERR(chbs))
+ dev_dbg(host, "No CHBS found for Host Bridge: %s\n",
+ dev_name(match));
+
+ rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
if (rc) {
dev_err(host, "failed to add downstream port: %s\n",
dev_name(match));
@@ -172,6 +252,7 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
static int cxl_acpi_probe(struct platform_device *pdev)
{
int rc;
+ acpi_status status;
struct cxl_port *root_port;
struct device *host = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(host);
@@ -181,10 +262,14 @@ static int cxl_acpi_probe(struct platform_device *pdev)
return PTR_ERR(root_port);
dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));

+ status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);
+ if (ACPI_FAILURE(status))
+ return -ENXIO;
+
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
add_host_bridge_dport);
if (rc)
- return rc;
+ goto out;

/*
* Root level scanned with host-bridge as dports, now scan host-bridges
@@ -193,11 +278,14 @@ static int cxl_acpi_probe(struct platform_device *pdev)
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
add_host_bridge_uport);
if (rc)
- return rc;
+ goto out;

if (IS_ENABLED(CONFIG_CXL_PMEM))
rc = device_for_each_child(&root_port->dev, root_port,
add_root_nvdimm_bridge);
+
+out:
+ acpi_put_table(acpi_cedt);
if (rc < 0)
return rc;
return 0;
--
2.26.2

2021-06-17 02:45:52

by Alison Schofield

[permalink] [raw]
Subject: [PATCH v3 2/2] cxl/acpi: Use the ACPI CFMWS to create static decoder objects

The ACPI CXL Early Discovery Table (CEDT) includes a list of CXL memory
resources in CXL Fixed Memory Window Structures (CFMWS). Retrieve each
CFMWS in the CEDT and add a cxl_decoder object to the root port (root0)
for each memory resource.

Signed-off-by: Alison Schofield <[email protected]>
---
drivers/cxl/acpi.c | 119 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)

diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 852b5c270464..018f5ac73b78 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -10,6 +10,123 @@

static struct acpi_table_header *acpi_cedt;

+/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
+#define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways)
+#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
+
+static unsigned long cfmws_to_decoder_flags(int restrictions)
+{
+ unsigned long flags = 0;
+
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
+ flags |= CXL_DECODER_F_TYPE2;
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
+ flags |= CXL_DECODER_F_TYPE3;
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
+ flags |= CXL_DECODER_F_RAM;
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
+ flags |= CXL_DECODER_F_PMEM;
+ if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
+ flags |= CXL_DECODER_F_LOCK;
+
+ return flags;
+}
+
+static int cxl_acpi_cfmws_verify(struct device *dev,
+ struct acpi_cedt_cfmws *cfmws)
+{
+ int expected_len;
+
+ if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
+ dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
+ dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
+ dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
+ return -EINVAL;
+ }
+
+ expected_len = struct_size((cfmws), interleave_targets,
+ CFMWS_INTERLEAVE_WAYS(cfmws));
+
+ if (cfmws->header.length < expected_len) {
+ dev_err(dev, "CFMWS length %d less than expected %d\n",
+ cfmws->header.length, expected_len);
+ return -EINVAL;
+ }
+
+ if (cfmws->header.length > expected_len)
+ dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
+ cfmws->header.length, expected_len);
+
+ return 0;
+}
+
+static void cxl_add_cfmws_decoders(struct device *dev,
+ struct cxl_port *root_port)
+{
+ struct acpi_cedt_cfmws *cfmws;
+ struct cxl_decoder *cxld;
+ acpi_size len, cur = 0;
+ void *cedt_subtable;
+ int rc;
+
+ len = acpi_cedt->length - sizeof(*acpi_cedt);
+ cedt_subtable = acpi_cedt + 1;
+
+ while (cur < len) {
+ struct acpi_cedt_header *c = cedt_subtable + cur;
+
+ if (c->type != ACPI_CEDT_TYPE_CFMWS) {
+ cur += c->length;
+ continue;
+ }
+
+ cfmws = cedt_subtable + cur;
+
+ if (dev_WARN_ONCE(dev, cfmws->header.length < sizeof(*cfmws),
+ "CFMWS entry skipped: invalid length:%u\n",
+ cfmws->header.length)) {
+ cur += c->length;
+ continue;
+ }
+
+ rc = cxl_acpi_cfmws_verify(dev, cfmws);
+ if (rc) {
+ dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
+ cfmws->base_hpa, cfmws->base_hpa +
+ cfmws->window_size - 1);
+ cur += c->length;
+ continue;
+ }
+
+ cxld = devm_cxl_add_decoder(dev, root_port,
+ CFMWS_INTERLEAVE_WAYS(cfmws),
+ cfmws->base_hpa, cfmws->window_size,
+ CFMWS_INTERLEAVE_WAYS(cfmws),
+ CFMWS_INTERLEAVE_GRANULARITY(cfmws),
+ CXL_DECODER_EXPANDER,
+ cfmws_to_decoder_flags(cfmws->restrictions));
+
+ if (IS_ERR(cxld)) {
+ dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
+ cfmws->base_hpa, cfmws->base_hpa +
+ cfmws->window_size - 1);
+ } else {
+ dev_dbg(dev, "add: %s range %#llx-%#llx\n",
+ dev_name(&cxld->dev), cfmws->base_hpa,
+ cfmws->base_hpa + cfmws->window_size - 1);
+ }
+ cur += c->length;
+ }
+}
+
static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
{
struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
@@ -271,6 +388,8 @@ static int cxl_acpi_probe(struct platform_device *pdev)
if (rc)
goto out;

+ cxl_add_cfmws_decoders(host, root_port);
+
/*
* Root level scanned with host-bridge as dports, now scan host-bridges
* for their role as CXL uports to their CXL-capable PCIe Root Ports.
--
2.26.2

2021-06-17 09:02:56

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/2] cxl/acpi: Use the ACPI CFMWS to create static decoder objects

On Wed, 16 Jun 2021 18:11:08 -0700
Alison Schofield <[email protected]> wrote:

> The ACPI CXL Early Discovery Table (CEDT) includes a list of CXL memory
> resources in CXL Fixed Memory Window Structures (CFMWS). Retrieve each
> CFMWS in the CEDT and add a cxl_decoder object to the root port (root0)
> for each memory resource.
>
> Signed-off-by: Alison Schofield <[email protected]>

Reviewed-by: Jonathan Cameron <[email protected]>

> ---
> drivers/cxl/acpi.c | 119 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 119 insertions(+)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 852b5c270464..018f5ac73b78 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -10,6 +10,123 @@
>
> static struct acpi_table_header *acpi_cedt;
>
> +/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
> +#define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways)
> +#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
> +
> +static unsigned long cfmws_to_decoder_flags(int restrictions)
> +{
> + unsigned long flags = 0;
> +
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
> + flags |= CXL_DECODER_F_TYPE2;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
> + flags |= CXL_DECODER_F_TYPE3;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
> + flags |= CXL_DECODER_F_RAM;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
> + flags |= CXL_DECODER_F_PMEM;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
> + flags |= CXL_DECODER_F_LOCK;
> +
> + return flags;
> +}
> +
> +static int cxl_acpi_cfmws_verify(struct device *dev,
> + struct acpi_cedt_cfmws *cfmws)
> +{
> + int expected_len;
> +
> + if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
> + dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
> + dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
> + dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
> + return -EINVAL;
> + }
> +
> + expected_len = struct_size((cfmws), interleave_targets,
> + CFMWS_INTERLEAVE_WAYS(cfmws));
> +
> + if (cfmws->header.length < expected_len) {
> + dev_err(dev, "CFMWS length %d less than expected %d\n",
> + cfmws->header.length, expected_len);
> + return -EINVAL;
> + }
> +
> + if (cfmws->header.length > expected_len)
> + dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
> + cfmws->header.length, expected_len);
> +
> + return 0;
> +}
> +
> +static void cxl_add_cfmws_decoders(struct device *dev,
> + struct cxl_port *root_port)
> +{
> + struct acpi_cedt_cfmws *cfmws;
> + struct cxl_decoder *cxld;
> + acpi_size len, cur = 0;
> + void *cedt_subtable;
> + int rc;
> +
> + len = acpi_cedt->length - sizeof(*acpi_cedt);
> + cedt_subtable = acpi_cedt + 1;
> +
> + while (cur < len) {
> + struct acpi_cedt_header *c = cedt_subtable + cur;
> +
> + if (c->type != ACPI_CEDT_TYPE_CFMWS) {
> + cur += c->length;
> + continue;
> + }
> +
> + cfmws = cedt_subtable + cur;
> +
> + if (dev_WARN_ONCE(dev, cfmws->header.length < sizeof(*cfmws),
> + "CFMWS entry skipped: invalid length:%u\n",
> + cfmws->header.length)) {
> + cur += c->length;
> + continue;
> + }
> +
> + rc = cxl_acpi_cfmws_verify(dev, cfmws);
> + if (rc) {
> + dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
> + cfmws->base_hpa, cfmws->base_hpa +
> + cfmws->window_size - 1);
> + cur += c->length;
> + continue;
> + }
> +
> + cxld = devm_cxl_add_decoder(dev, root_port,
> + CFMWS_INTERLEAVE_WAYS(cfmws),
> + cfmws->base_hpa, cfmws->window_size,
> + CFMWS_INTERLEAVE_WAYS(cfmws),
> + CFMWS_INTERLEAVE_GRANULARITY(cfmws),
> + CXL_DECODER_EXPANDER,
> + cfmws_to_decoder_flags(cfmws->restrictions));
> +
> + if (IS_ERR(cxld)) {
> + dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
> + cfmws->base_hpa, cfmws->base_hpa +
> + cfmws->window_size - 1);

This sounds rather fatal... I guess it'll be exposed later as we won't have
any means of using the decoder so we don't have to error out here though.

> + } else {
> + dev_dbg(dev, "add: %s range %#llx-%#llx\n",
> + dev_name(&cxld->dev), cfmws->base_hpa,
> + cfmws->base_hpa + cfmws->window_size - 1);
> + }
> + cur += c->length;
> + }
> +}
> +
> static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
> {
> struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
> @@ -271,6 +388,8 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> if (rc)
> goto out;
>
> + cxl_add_cfmws_decoders(host, root_port);
> +
> /*
> * Root level scanned with host-bridge as dports, now scan host-bridges
> * for their role as CXL uports to their CXL-capable PCIe Root Ports.

2021-06-17 09:50:12

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 1/2] cxl/acpi: Add the Host Bridge base address to CXL port objects

On Wed, 16 Jun 2021 18:11:07 -0700
Alison Schofield <[email protected]> wrote:

> The base address for the Host Bridge port component registers is located
> in the CXL Host Bridge Structure (CHBS) of the ACPI CXL Early Discovery
> Table (CEDT). Retrieve the CHBS for each Host Bridge (ACPI0016 device)
> and include that base address in the port object.
>
> Co-developed-by: Vishal Verma <[email protected]>
> Signed-off-by: Vishal Verma <[email protected]>
> Signed-off-by: Alison Schofield <[email protected]>

Reviewed-by: Jonathan Cameron <[email protected]>

> ---
> drivers/cxl/acpi.c | 98 +++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 93 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 8a723f7f3f73..852b5c270464 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -8,6 +8,58 @@
> #include <linux/pci.h>
> #include "cxl.h"
>
> +static struct acpi_table_header *acpi_cedt;
> +
> +static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
> +{
> + struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
> + acpi_size len, cur = 0;
> + void *cedt_subtable;
> +
> + len = acpi_cedt->length - sizeof(*acpi_cedt);
> + cedt_subtable = acpi_cedt + 1;
> +
> + while (cur < len) {
> + struct acpi_cedt_header *c = cedt_subtable + cur;
> +
> + if (c->type != ACPI_CEDT_TYPE_CHBS) {
> + cur += c->length;
> + continue;
> + }
> +
> + chbs = cedt_subtable + cur;
> +
> + if (dev_WARN_ONCE(dev, chbs->header.length < sizeof(*chbs),
> + "CHBS entry skipped: invalid length:%u\n",
> + chbs->header.length)) {
> + cur += c->length;
> + continue;
> + }
> +
> + if (chbs->uid != uid) {
> + cur += c->length;
> + continue;
> + }
> +
> + if (dev_WARN_ONCE(dev, chbs_match,
> + "CHBS entry skipped: duplicate UID:%u\n",
> + uid)) {
> + cur += c->length;
> + continue;
> + }
> +
> + chbs_match = chbs;
> + cur += c->length;
> + }
> +
> + return chbs_match ? chbs_match : ERR_PTR(-ENODEV);
> +}
> +
> +static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
> +{
> + return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
> +}
> +
> struct cxl_walk_context {
> struct device *dev;
> struct pci_bus *root;
> @@ -50,6 +102,21 @@ static int match_add_root_ports(struct pci_dev *pdev, void *data)
> return 0;
> }
>
> +static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device *dev)
> +{
> + struct cxl_dport *dport;
> +
> + device_lock(&port->dev);
> + list_for_each_entry(dport, &port->dports, list)
> + if (dport->dport == dev) {
> + device_unlock(&port->dev);
> + return dport;
> + }
> +
> + device_unlock(&port->dev);
> + return NULL;
> +}
> +
> static struct acpi_device *to_cxl_host_bridge(struct device *dev)
> {
> struct acpi_device *adev = to_acpi_device(dev);
> @@ -71,6 +138,7 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> struct acpi_pci_root *pci_root;
> struct cxl_walk_context ctx;
> struct cxl_decoder *cxld;
> + struct cxl_dport *dport;
> struct cxl_port *port;
>
> if (!bridge)
> @@ -80,8 +148,14 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> if (!pci_root)
> return -ENXIO;
>
> - /* TODO: fold in CEDT.CHBS retrieval */
> - port = devm_cxl_add_port(host, match, CXL_RESOURCE_NONE, root_port);
> + dport = find_dport_by_dev(root_port, match);
> + if (!dport) {
> + dev_dbg(host, "host bridge expected and not found\n");
> + return -ENODEV;
> + }
> +
> + port = devm_cxl_add_port(host, match, dport->component_reg_phys,
> + root_port);
> if (IS_ERR(port))
> return PTR_ERR(port);
> dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
> @@ -120,6 +194,7 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> int rc;
> acpi_status status;
> unsigned long long uid;
> + struct acpi_cedt_chbs *chbs;
> struct cxl_port *root_port = arg;
> struct device *host = root_port->dev.parent;
> struct acpi_device *bridge = to_cxl_host_bridge(match);
> @@ -135,7 +210,12 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> return -ENODEV;
> }
>
> - rc = cxl_add_dport(root_port, match, uid, CXL_RESOURCE_NONE);
> + chbs = cxl_acpi_match_chbs(host, uid);
> + if (IS_ERR(chbs))
> + dev_dbg(host, "No CHBS found for Host Bridge: %s\n",
> + dev_name(match));
> +
> + rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
> if (rc) {
> dev_err(host, "failed to add downstream port: %s\n",
> dev_name(match));
> @@ -172,6 +252,7 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
> static int cxl_acpi_probe(struct platform_device *pdev)
> {
> int rc;
> + acpi_status status;
> struct cxl_port *root_port;
> struct device *host = &pdev->dev;
> struct acpi_device *adev = ACPI_COMPANION(host);
> @@ -181,10 +262,14 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> return PTR_ERR(root_port);
> dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
>
> + status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);
> + if (ACPI_FAILURE(status))
> + return -ENXIO;
> +
> rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
> add_host_bridge_dport);
> if (rc)
> - return rc;
> + goto out;
>
> /*
> * Root level scanned with host-bridge as dports, now scan host-bridges
> @@ -193,11 +278,14 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
> add_host_bridge_uport);
> if (rc)
> - return rc;
> + goto out;
>
> if (IS_ENABLED(CONFIG_CXL_PMEM))
> rc = device_for_each_child(&root_port->dev, root_port,
> add_root_nvdimm_bridge);
> +
> +out:
> + acpi_put_table(acpi_cedt);
> if (rc < 0)
> return rc;
> return 0;

2021-06-17 18:30:55

by Ben Widawsky

[permalink] [raw]
Subject: Re: [PATCH v3 0/2] CXL ACPI tables for object creation

On 21-06-16 18:11:06, Alison Schofield wrote:
> Jonathan - I updated Patch 2 so I didn't keep your Reviewed-by tag.
>
> Changes since v2 [1]:
> - Warn and continue, rather than error out, on these acpi table parsing issues:
> table length mismatch for either CHBS or CFMWS; duplicate uid's for CHBS.
> (Ben, Jonathan)
> - Update flow in cxl_acpi_match_chbs() (Ben, Jonathan)
> - Improve naming cedt_table->acpi_cedt, cedt_base->cedt_subtable (Ben)
> - Emit debug message only if CFMWS is greater than its expected length (Ben)
> - Update the dev_err messages wrt the CFMWS expected length failure.
> - Remove blank line before error handling block (Jonathan)
> - Rebase to the CXL pending branch [2]
>
> [1]: https://lore.kernel.org/linux-cxl/[email protected]/
> [2]: https://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl.git/log/?h=pending
>
> --
> Parse the ACPI CXL Early Discovery Table (CEDT) and use the CHBS & CFMWS
> when creating port and decoder objects.
>
> CHBS: CXL Host Bridge Structure - Patch 1
> CFMWS: CXL Fixed Memory Window Structure - Patch 2
>
> Alison Schofield (2):
> cxl/acpi: Add the Host Bridge base address to CXL port objects
> cxl/acpi: Use the ACPI CFMWS to create static decoder objects
>
> drivers/cxl/acpi.c | 217 +++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 212 insertions(+), 5 deletions(-)
>
>
> base-commit: 21083f51521fb0f60dbac591f175c3ed48435af4

It might be nice to add a header kdoc for acpi.c now that it's become sufficiently
complex. Can be follow-on patch.

Both are:
Reviewed-by: Ben Widawsky <[email protected]>

2021-06-17 19:20:46

by Ben Widawsky

[permalink] [raw]
Subject: Re: [PATCH v3 2/2] cxl/acpi: Use the ACPI CFMWS to create static decoder objects

On 21-06-16 18:11:08, Alison Schofield wrote:
> The ACPI CXL Early Discovery Table (CEDT) includes a list of CXL memory
> resources in CXL Fixed Memory Window Structures (CFMWS). Retrieve each
> CFMWS in the CEDT and add a cxl_decoder object to the root port (root0)
> for each memory resource.
>
> Signed-off-by: Alison Schofield <[email protected]>
> ---
> drivers/cxl/acpi.c | 119 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 119 insertions(+)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 852b5c270464..018f5ac73b78 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -10,6 +10,123 @@
>
> static struct acpi_table_header *acpi_cedt;
>
> +/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
> +#define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways)
> +#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
> +
> +static unsigned long cfmws_to_decoder_flags(int restrictions)
> +{
> + unsigned long flags = 0;
> +
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
> + flags |= CXL_DECODER_F_TYPE2;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE3)
> + flags |= CXL_DECODER_F_TYPE3;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE)
> + flags |= CXL_DECODER_F_RAM;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_PMEM)
> + flags |= CXL_DECODER_F_PMEM;
> + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED)
> + flags |= CXL_DECODER_F_LOCK;
> +
> + return flags;
> +}
> +
> +static int cxl_acpi_cfmws_verify(struct device *dev,
> + struct acpi_cedt_cfmws *cfmws)
> +{
> + int expected_len;
> +
> + if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) {
> + dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n");
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(cfmws->base_hpa, SZ_256M)) {
> + dev_err(dev, "CFMWS Base HPA not 256MB aligned\n");
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(cfmws->window_size, SZ_256M)) {
> + dev_err(dev, "CFMWS Window Size not 256MB aligned\n");
> + return -EINVAL;
> + }
> +
> + expected_len = struct_size((cfmws), interleave_targets,
> + CFMWS_INTERLEAVE_WAYS(cfmws));
> +
> + if (cfmws->header.length < expected_len) {
> + dev_err(dev, "CFMWS length %d less than expected %d\n",
> + cfmws->header.length, expected_len);
> + return -EINVAL;
> + }
> +
> + if (cfmws->header.length > expected_len)
> + dev_dbg(dev, "CFMWS length %d greater than expected %d\n",
> + cfmws->header.length, expected_len);
> +
> + return 0;
> +}
> +
> +static void cxl_add_cfmws_decoders(struct device *dev,
> + struct cxl_port *root_port)
> +{
> + struct acpi_cedt_cfmws *cfmws;
> + struct cxl_decoder *cxld;
> + acpi_size len, cur = 0;
> + void *cedt_subtable;
> + int rc;
> +
> + len = acpi_cedt->length - sizeof(*acpi_cedt);
> + cedt_subtable = acpi_cedt + 1;
> +
> + while (cur < len) {
> + struct acpi_cedt_header *c = cedt_subtable + cur;
> +
> + if (c->type != ACPI_CEDT_TYPE_CFMWS) {
> + cur += c->length;
> + continue;
> + }
> +
> + cfmws = cedt_subtable + cur;
> +
> + if (dev_WARN_ONCE(dev, cfmws->header.length < sizeof(*cfmws),
> + "CFMWS entry skipped: invalid length:%u\n",
> + cfmws->header.length)) {
> + cur += c->length;
> + continue;
> + }
> +
> + rc = cxl_acpi_cfmws_verify(dev, cfmws);
> + if (rc) {
> + dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
> + cfmws->base_hpa, cfmws->base_hpa +
> + cfmws->window_size - 1);
> + cur += c->length;
> + continue;
> + }
> +
> + cxld = devm_cxl_add_decoder(dev, root_port,
> + CFMWS_INTERLEAVE_WAYS(cfmws),
> + cfmws->base_hpa, cfmws->window_size,
> + CFMWS_INTERLEAVE_WAYS(cfmws),
> + CFMWS_INTERLEAVE_GRANULARITY(cfmws),
> + CXL_DECODER_EXPANDER,
> + cfmws_to_decoder_flags(cfmws->restrictions));

checkpatch complains here for me.

CHECK: Alignment should match open parenthesis
#122: FILE: drivers/cxl/acpi.c:110:
+ cxld = devm_cxl_add_decoder(dev, root_port,
+ CFMWS_INTERLEAVE_WAYS(cfmws),


> +
> + if (IS_ERR(cxld)) {
> + dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
> + cfmws->base_hpa, cfmws->base_hpa +
> + cfmws->window_size - 1);
> + } else {
> + dev_dbg(dev, "add: %s range %#llx-%#llx\n",
> + dev_name(&cxld->dev), cfmws->base_hpa,
> + cfmws->base_hpa + cfmws->window_size - 1);
> + }
> + cur += c->length;
> + }
> +}
> +
> static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
> {
> struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
> @@ -271,6 +388,8 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> if (rc)
> goto out;
>
> + cxl_add_cfmws_decoders(host, root_port);
> +
> /*
> * Root level scanned with host-bridge as dports, now scan host-bridges
> * for their role as CXL uports to their CXL-capable PCIe Root Ports.
> --
> 2.26.2
>