linux-cxl.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/13] Enumerate midlevel and endpoint decoders
@ 2021-09-02 19:50 Ben Widawsky
  2021-09-02 19:50 ` [PATCH 01/13] Documentation/cxl: Add bus internal docs Ben Widawsky
                   ` (13 more replies)
  0 siblings, 14 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Every CXL component may implement component registers as defined by the CXL 2.0
specification. In preparation for creating and enumerating regions it's
important to at least enumerate all HDM decoders which are a subset of the
component registers. To do this, a new cxl_mem driver is introduced which is
responsible for binding to a CXL.mem enabled device. In order to determine
whether or not an endpoint is CXL enabled, the relevant subhierarchy must be
enumerated.

This serves as the stepping stone toward enabling regions because regions must
be able to determine if the devices selected for the region are CXL.mem capable
and enabled.

There's two issues that need to be resolved but I'm going to propose we fix
them next time we need to touch this code...
1. cxl_pci now relinquishes its component register mappings. This may be
   undesirable as cxl_pci may need to use those mappings.
2a. some amount of component register enumeration is duplicated in cxl_pci and
    cxl_mem
2b. awkwardness in cxl_mem where memdevs get their component registers from
   cxl_pci, and ports that enumerate their own component registers

The obvious fix for both of these is to move component register mapping to
cxl_core, and let cxl_core arbitrate the mappings for the "client" drivers.
Since the code needed to enable cxl_mem was small and subset of the existing
code (and fairly error resistent vs creating a cxl_core API) I'm hoping to kick
the can down the road.

NOTE: I do not have a way at present to test switches. For this reason and for
patch readability, the switch enumeration is left as a separate patch.

NOTE2: Some of these patches were introduced in an RFC for region creation. Upon
further inspection, it made a lot of sense to land these before region creation
so long as it's understood upfront why the new driver is needed.

I've pushed this to my gitlab here:
https://gitlab.com/bwidawsk/linux/-/tree/decoders

Ben Widawsky (13):
  Documentation/cxl: Add bus internal docs
  cxl/core/bus: Add kernel docs for decoder ops
  cxl/core: Ignore interleave when adding decoders
  cxl: Introduce endpoint decoders
  cxl/pci: Disambiguate cxl_pci further from cxl_mem
  cxl/mem: Introduce cxl_mem driver
  cxl/memdev: Determine CXL.mem capability
  cxl/mem: Add memdev as a port
  cxl/pci: Retain map information in cxl_mem_probe
  cxl/core: Map component registers for ports
  cxl/core: Convert decoder range to resource
  cxl/core/bus: Enumerate all HDM decoders
  cxl/mem: Enumerate switch decoders

 .../driver-api/cxl/memory-devices.rst         |   6 +
 drivers/cxl/Makefile                          |   3 +-
 drivers/cxl/acpi.c                            |  39 +--
 drivers/cxl/core/bus.c                        | 326 +++++++++++++++++-
 drivers/cxl/core/core.h                       |   1 +
 drivers/cxl/core/memdev.c                     |  19 +-
 drivers/cxl/core/regs.c                       |   6 +-
 drivers/cxl/cxl.h                             |  65 +++-
 drivers/cxl/cxlmem.h                          |   6 +-
 drivers/cxl/mem.c                             | 237 +++++++++++++
 drivers/cxl/pci.c                             | 124 +++----
 drivers/cxl/pci.h                             |  15 +-
 12 files changed, 727 insertions(+), 120 deletions(-)
 create mode 100644 drivers/cxl/mem.c

-- 
2.33.0


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH 01/13] Documentation/cxl: Add bus internal docs
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:05   ` Jonathan Cameron
  2021-09-02 19:50 ` [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops Ben Widawsky
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Kernel docs are already present in this file, but nothing is instructed
to generate them. Address that.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 Documentation/driver-api/cxl/memory-devices.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
index 356f70d28316..a18175bae7a6 100644
--- a/Documentation/driver-api/cxl/memory-devices.rst
+++ b/Documentation/driver-api/cxl/memory-devices.rst
@@ -39,6 +39,9 @@ CXL Core
 .. kernel-doc:: drivers/cxl/core/bus.c
    :doc: cxl core
 
+.. kernel-doc:: drivers/cxl/core/bus.c
+   :identifiers:
+
 .. kernel-doc:: drivers/cxl/core/pmem.c
    :internal:
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
  2021-09-02 19:50 ` [PATCH 01/13] Documentation/cxl: Add bus internal docs Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:17   ` Jonathan Cameron
  2021-09-10 18:51   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 03/13] cxl/core: Ignore interleave when adding decoders Ben Widawsky
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Since the code to add decoders for switches and endpoints is on the
horizon, document the new interfaces that will be consumed by them.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 3991ac231c3e..9d98dd50d424 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -453,6 +453,19 @@ int cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id,
 }
 EXPORT_SYMBOL_GPL(cxl_add_dport);
 
+/**
+ * cxl_decoder_alloc - Allocate a new CXL decoder
+ * @port: owning port of this decoder
+ * @nr_targets: downstream targets accessible by this decoder
+ *
+ * A port should contain one or more decoders. Each of those decoders enable
+ * some address space for CXL.mem utilization. Therefore, it is logical to
+ * allocate decoders while enumerating a port. While >= 1 is defined by the CXL
+ * specification, due to error conditions it is possible that a port may have 0
+ * decoders.
+ *
+ * Return: A new cxl decoder which wants to be added with cxl_decoder_add()
+ */
 struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
 {
 	struct cxl_decoder *cxld;
@@ -491,6 +504,21 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
 }
 EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
 
+/**
+ * cxl_decoder_add - Add a decoder with targets
+ * @host: The containing struct device. This is typically the PCI device that is
+ *        CXL capable
+ * @cxld: The cxl decoder allocated by cxl_decoder_alloc()
+ * @target_map: A list of downstream ports that this decoder can direct memory
+ *              traffic to. These numbers should correspond with the port number
+ *              in the PCIe Link Capabilities structure.
+ *
+ * Return: 0 if decoder was successfully added.
+ *
+ * Certain types of decoders may not have any targets. The main example of this
+ * is an endpoint device. A more awkward example is a hostbridge whose root
+ * ports get hot added (technically possible, though unlikely).
+ */
 int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
 		    int *target_map)
 {
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 03/13] cxl/core: Ignore interleave when adding decoders
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
  2021-09-02 19:50 ` [PATCH 01/13] Documentation/cxl: Add bus internal docs Ben Widawsky
  2021-09-02 19:50 ` [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:25   ` Jonathan Cameron
  2021-09-02 19:50 ` [PATCH 04/13] cxl: Introduce endpoint decoders Ben Widawsky
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Decoders will be added to the bus either already active (committed in
spec parlance), or inactive. From the driver perspective, the set of
devices comprising the former are those which are brought up by system
firmware; decoders that implement: volatile regions, persistent regions,
or platform specific (ie. CFMWS) constraints. Such devices have a given
interleave programming already in place. Inactive decoders on the other
hand, do not have any interleave programming in place. The set of
devices comprising that are hostbridges, switches, and endpoint devices.

Allow adding inactive decoders by removing this check.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 9d98dd50d424..8d5061b0794d 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -532,9 +532,6 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
 	if (IS_ERR(cxld))
 		return PTR_ERR(cxld);
 
-	if (cxld->interleave_ways < 1)
-		return -EINVAL;
-
 	port = to_cxl_port(cxld->dev.parent);
 	device_lock(&port->dev);
 	if (list_empty(&port->dports)) {
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (2 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 03/13] cxl/core: Ignore interleave when adding decoders Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:35   ` Jonathan Cameron
  2021-09-10 19:19   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem Ben Widawsky
                   ` (9 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Endpoints have decoders too. It is useful to share the same
infrastructure from cxl_core. Endpoints do not have dports (downstream
targets), only the underlying physical medium. As a result, some special
casing is needed.

There is no functional change introduced yet as endpoints don't actually
enumerate decoders yet.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 8d5061b0794d..6202ce5a5ac2 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
 	NULL,
 };
 
+static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
+	&cxl_decoder_base_attribute_group,
+	&cxl_base_attribute_group,
+	NULL,
+};
+
 static void cxl_decoder_release(struct device *dev)
 {
 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
@@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
 	kfree(cxld);
 }
 
+static const struct device_type cxl_decoder_endpoint_type = {
+	.name = "cxl_decoder_endpoint",
+	.release = cxl_decoder_release,
+	.groups = cxl_decoder_endpoint_attribute_groups,
+};
+
 static const struct device_type cxl_decoder_switch_type = {
 	.name = "cxl_decoder_switch",
 	.release = cxl_decoder_release,
@@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
 	.groups = cxl_decoder_root_attribute_groups,
 };
 
+static bool is_endpoint_decoder(struct device *dev)
+{
+	return dev->type == &cxl_decoder_endpoint_type;
+}
+
 bool is_root_decoder(struct device *dev)
 {
 	return dev->type == &cxl_decoder_root_type;
@@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
 	struct device *dev;
 	int rc = 0;
 
-	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
+	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
 		return ERR_PTR(-EINVAL);
 
 	cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
@@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
 	dev->parent = &port->dev;
 	dev->bus = &cxl_bus_type;
 
+	/* Endpoints don't have a target list */
+	if (nr_targets == 0)
+		dev->type = &cxl_decoder_endpoint_type;
 	/* root ports do not have a cxl_port_type parent */
-	if (port->dev.parent->type == &cxl_port_type)
+	else if (port->dev.parent->type == &cxl_port_type)
 		dev->type = &cxl_decoder_switch_type;
 	else
 		dev->type = &cxl_decoder_root_type;
@@ -532,9 +552,11 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
 	if (IS_ERR(cxld))
 		return PTR_ERR(cxld);
 
+	dev = &cxld->dev;
+
 	port = to_cxl_port(cxld->dev.parent);
 	device_lock(&port->dev);
-	if (list_empty(&port->dports)) {
+	if (is_endpoint_decoder(dev) && list_empty(&port->dports)) {
 		rc = -EINVAL;
 		goto out_unlock;
 	}
@@ -551,7 +573,6 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
 	}
 	device_unlock(&port->dev);
 
-	dev = &cxld->dev;
 	rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
 	if (rc)
 		return rc;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (3 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 04/13] cxl: Introduce endpoint decoders Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:45   ` Jonathan Cameron
  2021-09-10 19:27   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 06/13] cxl/mem: Introduce cxl_mem driver Ben Widawsky
                   ` (8 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Commit 21e9f76733a8 ("cxl: Rename mem to pci") introduced the cxl_pci
driver which had formerly been named cxl_mem. At the time, the goal was
to be as light touch as possible because there were other patches in
flight. Since things have settled now, and a new cxl_mem driver will be
introduced shortly, spend the LOC now to clean up the existing names.

While here, fix the kernel docs to explain the situation better after
the core rework that has already landed.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/pci.c | 70 +++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 35 deletions(-)

diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index b13884275d96..6931885c83ce 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -16,14 +16,14 @@
  *
  * This implements the PCI exclusive functionality for a CXL device as it is
  * defined by the Compute Express Link specification. CXL devices may surface
- * certain functionality even if it isn't CXL enabled.
+ * certain functionality even if it isn't CXL enabled. While this driver is
+ * focused around the PCI specific aspects of a CXL device, it binds to the
+ * specific CXL memory device class code, and therefore the implementation of
+ * cxl_pci is focused around CXL memory devices.
  *
- * The driver has several responsibilities, mainly:
+ * The driver has two responsibilities:
  *  - Create the memX device and register on the CXL bus.
  *  - Enumerate device's register interface and map them.
- *  - Probe the device attributes to establish sysfs interface.
- *  - Provide an IOCTL interface to userspace to communicate with the device for
- *    things like firmware update.
  */
 
 #define cxl_doorbell_busy(cxlm)                                                \
@@ -33,7 +33,7 @@
 /* CXL 2.0 - 8.2.8.4 */
 #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
 
-static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
+static int cxl_pci_wait_for_doorbell(struct cxl_mem *cxlm)
 {
 	const unsigned long start = jiffies;
 	unsigned long end = start;
@@ -55,7 +55,7 @@ static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
 	return 0;
 }
 
-static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
+static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
 				 struct cxl_mbox_cmd *mbox_cmd)
 {
 	struct device *dev = cxlm->dev;
@@ -65,7 +65,7 @@ static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
 }
 
 /**
- * __cxl_mem_mbox_send_cmd() - Execute a mailbox command
+ * __cxl_pci_mbox_send_cmd() - Execute a mailbox command
  * @cxlm: The CXL memory device to communicate with.
  * @mbox_cmd: Command to send to the memory device.
  *
@@ -86,7 +86,7 @@ static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
  * not need to coordinate with each other. The driver only uses the primary
  * mailbox.
  */
-static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,
+static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
 				   struct cxl_mbox_cmd *mbox_cmd)
 {
 	void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
@@ -140,9 +140,9 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,
 	       cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
 
 	/* #5 */
-	rc = cxl_mem_wait_for_doorbell(cxlm);
+	rc = cxl_pci_wait_for_doorbell(cxlm);
 	if (rc == -ETIMEDOUT) {
-		cxl_mem_mbox_timeout(cxlm, mbox_cmd);
+		cxl_pci_mbox_timeout(cxlm, mbox_cmd);
 		return rc;
 	}
 
@@ -181,13 +181,13 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,
 }
 
 /**
- * cxl_mem_mbox_get() - Acquire exclusive access to the mailbox.
+ * cxl_pci_mbox_get() - Acquire exclusive access to the mailbox.
  * @cxlm: The memory device to gain access to.
  *
  * Context: Any context. Takes the mbox_mutex.
  * Return: 0 if exclusive access was acquired.
  */
-static int cxl_mem_mbox_get(struct cxl_mem *cxlm)
+static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
 {
 	struct device *dev = cxlm->dev;
 	u64 md_status;
@@ -212,7 +212,7 @@ static int cxl_mem_mbox_get(struct cxl_mem *cxlm)
 	 *    Mailbox Interface Ready bit. Therefore, waiting for the doorbell
 	 *    to be ready is sufficient.
 	 */
-	rc = cxl_mem_wait_for_doorbell(cxlm);
+	rc = cxl_pci_wait_for_doorbell(cxlm);
 	if (rc) {
 		dev_warn(dev, "Mailbox interface not ready\n");
 		goto out;
@@ -252,12 +252,12 @@ static int cxl_mem_mbox_get(struct cxl_mem *cxlm)
 }
 
 /**
- * cxl_mem_mbox_put() - Release exclusive access to the mailbox.
+ * cxl_pci_mbox_put() - Release exclusive access to the mailbox.
  * @cxlm: The CXL memory device to communicate with.
  *
  * Context: Any context. Expects mbox_mutex to be held.
  */
-static void cxl_mem_mbox_put(struct cxl_mem *cxlm)
+static void cxl_pci_mbox_put(struct cxl_mem *cxlm)
 {
 	mutex_unlock(&cxlm->mbox_mutex);
 }
@@ -266,17 +266,17 @@ static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
 {
 	int rc;
 
-	rc = cxl_mem_mbox_get(cxlm);
+	rc = cxl_pci_mbox_get(cxlm);
 	if (rc)
 		return rc;
 
-	rc = __cxl_mem_mbox_send_cmd(cxlm, cmd);
-	cxl_mem_mbox_put(cxlm);
+	rc = __cxl_pci_mbox_send_cmd(cxlm, cmd);
+	cxl_pci_mbox_put(cxlm);
 
 	return rc;
 }
 
-static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
+static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm)
 {
 	const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
 
@@ -304,7 +304,7 @@ static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
 	return 0;
 }
 
-static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
+static void __iomem *cxl_pci_map_regblock(struct cxl_mem *cxlm,
 					  u8 bar, u64 offset)
 {
 	void __iomem *addr;
@@ -330,12 +330,12 @@ static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
 	return addr;
 }
 
-static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
+static void cxl_pci_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
 {
 	pci_iounmap(to_pci_dev(cxlm->dev), base);
 }
 
-static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
+static int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
 {
 	int pos;
 
@@ -428,7 +428,7 @@ static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
 }
 
 /**
- * cxl_mem_setup_regs() - Setup necessary MMIO.
+ * cxl_pci_setup_regs() - Setup necessary MMIO.
  * @cxlm: The CXL memory device to communicate with.
  *
  * Return: 0 if all necessary registers mapped.
@@ -437,7 +437,7 @@ static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
  * regions. The purpose of this function is to enumerate and map those
  * registers.
  */
-static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
+static int cxl_pci_setup_regs(struct cxl_mem *cxlm)
 {
 	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
 	struct device *dev = cxlm->dev;
@@ -447,7 +447,7 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
 	struct cxl_register_map *map, maps[CXL_REGLOC_RBI_TYPES];
 	int ret = 0;
 
-	regloc = cxl_mem_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
+	regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
 	if (!regloc) {
 		dev_err(dev, "register location dvsec not found\n");
 		return -ENXIO;
@@ -482,7 +482,7 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
 		if (reg_type > CXL_REGLOC_RBI_MEMDEV)
 			continue;
 
-		base = cxl_mem_map_regblock(cxlm, bar, offset);
+		base = cxl_pci_map_regblock(cxlm, bar, offset);
 		if (!base)
 			return -ENOMEM;
 
@@ -494,7 +494,7 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
 		ret = cxl_probe_regs(cxlm, base + offset, map);
 
 		/* Always unmap the regblock regardless of probe success */
-		cxl_mem_unmap_regblock(cxlm, base);
+		cxl_pci_unmap_regblock(cxlm, base);
 
 		if (ret)
 			return ret;
@@ -513,7 +513,7 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
 	return ret;
 }
 
-static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct cxl_memdev *cxlmd;
 	struct cxl_mem *cxlm;
@@ -534,11 +534,11 @@ static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (IS_ERR(cxlm))
 		return PTR_ERR(cxlm);
 
-	rc = cxl_mem_setup_regs(cxlm);
+	rc = cxl_pci_setup_regs(cxlm);
 	if (rc)
 		return rc;
 
-	rc = cxl_mem_setup_mailbox(cxlm);
+	rc = cxl_pci_setup_mailbox(cxlm);
 	if (rc)
 		return rc;
 
@@ -569,17 +569,17 @@ static const struct pci_device_id cxl_mem_pci_tbl[] = {
 	{ PCI_DEVICE_CLASS((PCI_CLASS_MEMORY_CXL << 8 | CXL_MEMORY_PROGIF), ~0)},
 	{ /* terminate list */ },
 };
-MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);
+MODULE_DEVICE_TABLE(pci, cxl_pci_tbl);
 
-static struct pci_driver cxl_mem_driver = {
+static struct pci_driver cxl_pci_driver = {
 	.name			= KBUILD_MODNAME,
 	.id_table		= cxl_mem_pci_tbl,
-	.probe			= cxl_mem_probe,
+	.probe			= cxl_pci_probe,
 	.driver	= {
 		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
 	},
 };
 
 MODULE_LICENSE("GPL v2");
-module_pci_driver(cxl_mem_driver);
+module_pci_driver(cxl_pci_driver);
 MODULE_IMPORT_NS(CXL);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 06/13] cxl/mem: Introduce cxl_mem driver
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (4 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 14:52   ` Jonathan Cameron
  2021-09-10 21:32   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 07/13] cxl/memdev: Determine CXL.mem capability Ben Widawsky
                   ` (7 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

CXL endpoints that participate in the CXL.mem protocol require extra
control to ensure architectural constraints are met for device
management. The most straight-forward way to achieve control of these
endpoints is with a new driver that can bind to such devices. This
driver will also be responsible for enumerating the switches that
connect the endpoint to the hostbridge.

cxl_core already understands the concept of a memdev, but the core [by
design] does not comprehend all the topological constraints.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 .../driver-api/cxl/memory-devices.rst         |  3 ++
 drivers/cxl/Makefile                          |  3 +-
 drivers/cxl/core/bus.c                        |  2 +
 drivers/cxl/core/core.h                       |  1 +
 drivers/cxl/core/memdev.c                     |  2 +-
 drivers/cxl/cxl.h                             |  1 +
 drivers/cxl/mem.c                             | 49 +++++++++++++++++++
 7 files changed, 59 insertions(+), 2 deletions(-)
 create mode 100644 drivers/cxl/mem.c

diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
index a18175bae7a6..00d141071570 100644
--- a/Documentation/driver-api/cxl/memory-devices.rst
+++ b/Documentation/driver-api/cxl/memory-devices.rst
@@ -28,6 +28,9 @@ CXL Memory Device
 .. kernel-doc:: drivers/cxl/pci.c
    :internal:
 
+.. kernel-doc:: drivers/cxl/mem.c
+   :doc: cxl mem
+
 CXL Core
 --------
 .. kernel-doc:: drivers/cxl/cxl.h
diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
index d1aaabc940f3..d912ac4e3f0c 100644
--- a/drivers/cxl/Makefile
+++ b/drivers/cxl/Makefile
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_CXL_BUS) += core/
-obj-$(CONFIG_CXL_MEM) += cxl_pci.o
+obj-$(CONFIG_CXL_MEM) += cxl_mem.o cxl_pci.o
 obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
 obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
 
+cxl_mem-y := mem.o
 cxl_pci-y := pci.o
 cxl_acpi-y := acpi.o
 cxl_pmem-y := pmem.o
diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 6202ce5a5ac2..256e55dc2a3b 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -641,6 +641,8 @@ static int cxl_device_id(struct device *dev)
 		return CXL_DEVICE_NVDIMM_BRIDGE;
 	if (dev->type == &cxl_nvdimm_type)
 		return CXL_DEVICE_NVDIMM;
+	if (dev->type == &cxl_memdev_type)
+		return CXL_DEVICE_ENDPOINT;
 	return 0;
 }
 
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index e0c9aacc4e9c..dea246cb7c58 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -6,6 +6,7 @@
 
 extern const struct device_type cxl_nvdimm_bridge_type;
 extern const struct device_type cxl_nvdimm_type;
+extern const struct device_type cxl_memdev_type;
 
 extern struct attribute_group cxl_base_attribute_group;
 
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index ee61202c7aab..c9dd054bd813 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -127,7 +127,7 @@ static const struct attribute_group *cxl_memdev_attribute_groups[] = {
 	NULL,
 };
 
-static const struct device_type cxl_memdev_type = {
+const struct device_type cxl_memdev_type = {
 	.name = "cxl_memdev",
 	.release = cxl_memdev_release,
 	.devnode = cxl_memdev_devnode,
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 708bfe92b596..b48bdbefd949 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -315,6 +315,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
 
 #define CXL_DEVICE_NVDIMM_BRIDGE	1
 #define CXL_DEVICE_NVDIMM		2
+#define CXL_DEVICE_ENDPOINT		3
 
 #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
 #define CXL_MODALIAS_FMT "cxl:t%d"
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
new file mode 100644
index 000000000000..978a54b0a51a
--- /dev/null
+++ b/drivers/cxl/mem.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "cxlmem.h"
+
+/**
+ * DOC: cxl mem
+ *
+ * CXL memory endpoint devices and switches are CXL capable devices that are
+ * participating in CXL.mem protocol. Their functionality builds on top of the
+ * CXL.io protocol that allows enumerating and configuring components via
+ * standard PCI mechanisms.
+ *
+ * The cxl_mem driver implements enumeration and control over these CXL
+ * components.
+ */
+
+static int cxl_mem_probe(struct device *dev)
+{
+	return -EOPNOTSUPP;
+}
+
+static void cxl_mem_remove(struct device *dev)
+{
+}
+
+static struct cxl_driver cxl_mem_driver = {
+	.name = "cxl_mem",
+	.probe = cxl_mem_probe,
+	.remove = cxl_mem_remove,
+	.id = CXL_DEVICE_ENDPOINT,
+};
+
+static __init int cxl_mem_init(void)
+{
+	return cxl_driver_register(&cxl_mem_driver);
+}
+
+static __exit void cxl_mem_exit(void)
+{
+	cxl_driver_unregister(&cxl_mem_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+module_init(cxl_mem_init);
+module_exit(cxl_mem_exit);
+MODULE_IMPORT_NS(CXL);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (5 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 06/13] cxl/mem: Introduce cxl_mem driver Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 15:21   ` Jonathan Cameron
  2021-09-10 21:59   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 08/13] cxl/mem: Add memdev as a port Ben Widawsky
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

If the "upstream" port of the endpoint is an enumerated downstream CXL
port, and the device itself is CXL capable and enabled, the memdev
driver can bind. This binding useful for region configuration/creation
because it provides a clean way for the region code to determine if the
memdev is actually CXL capable.

A memdev/hostbridge probe race is solved with a full CXL bus rescan at
the end of ACPI probing (see comment in code for details). Switch
enumeration will be done as a follow-on patch. As a result, if a switch
is in the topology the memdev driver will not bind to any devices.

CXL.mem capability is checked lazily at the time a region is bound.
This is in line with the other configuration parameters.

Below is an example (mem0, and mem1) of CXL memdev devices that now
exist on the bus.

/sys/bus/cxl/devices/
├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
└── root0 -> ../../../devices/platform/ACPI0017:00/root0

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/acpi.c        | 27 +++++++-----------
 drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
 drivers/cxl/core/memdev.c |  6 ++++
 drivers/cxl/cxl.h         |  2 ++
 drivers/cxl/cxlmem.h      |  2 ++
 drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
 drivers/cxl/pci.c         | 23 ---------------
 drivers/cxl/pci.h         |  7 ++++-
 8 files changed, 141 insertions(+), 41 deletions(-)

diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 7130beffc929..fd14094bdb3f 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -240,21 +240,6 @@ __mock 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;
-}
-
 __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
 					      struct device *dev)
 {
@@ -459,9 +444,19 @@ static int cxl_acpi_probe(struct platform_device *pdev)
 	if (rc)
 		goto out;
 
-	if (IS_ENABLED(CONFIG_CXL_PMEM))
+	if (IS_ENABLED(CONFIG_CXL_PMEM)) {
 		rc = device_for_each_child(&root_port->dev, root_port,
 					   add_root_nvdimm_bridge);
+		if (rc)
+			goto out;
+	}
+
+	/*
+	 * While ACPI is scanning hostbridge ports, switches and memory devices
+	 * may have been probed. Those devices will need to know whether the
+	 * hostbridge is CXL capable.
+	 */
+	rc = bus_rescan_devices(&cxl_bus_type);
 
 out:
 	acpi_put_table(acpi_cedt);
diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 256e55dc2a3b..56f57302d27b 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -8,6 +8,7 @@
 #include <linux/idr.h>
 #include <cxlmem.h>
 #include <cxl.h>
+#include <pci.h>
 #include "core.h"
 
 /**
@@ -259,6 +260,12 @@ static const struct device_type cxl_port_type = {
 	.groups = cxl_port_attribute_groups,
 };
 
+bool is_cxl_port(struct device *dev)
+{
+	return dev->type == &cxl_port_type;
+}
+EXPORT_SYMBOL_GPL(is_cxl_port);
+
 struct cxl_port *to_cxl_port(struct device *dev)
 {
 	if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
@@ -266,6 +273,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
 		return NULL;
 	return container_of(dev, struct cxl_port, dev);
 }
+EXPORT_SYMBOL_GPL(to_cxl_port);
 
 static void unregister_port(void *_port)
 {
@@ -424,6 +432,27 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
 	return dup ? -EEXIST : 0;
 }
 
+/**
+ * find_dport_by_dev - gets downstream CXL port from a struct device
+ * @port: cxl [upstream] port that "owns" the downstream port is being queried
+ * @dev: The device that is backing the downstream port
+ */
+struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const 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;
+}
+EXPORT_SYMBOL_GPL(find_dport_by_dev);
+
 /**
  * cxl_add_dport - append downstream port data to a cxl_port
  * @port: the cxl_port that references this dport
@@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
 }
 EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
 
+/**
+ * cxl_pci_dvsec - Gets offset for the given DVSEC id
+ * @pdev: PCI device to search for the DVSEC
+ * @dvsec: DVSEC id to look for
+ *
+ * Return: offset within the PCI header for the given DVSEC id. 0 if not found
+ */
+int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
+{
+	int pos;
+
+	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
+	if (!pos)
+		return 0;
+
+	while (pos) {
+		u16 vendor, id;
+
+		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
+		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
+		if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
+			return pos;
+
+		pos = pci_find_next_ext_capability(pdev, pos,
+						   PCI_EXT_CAP_ID_DVSEC);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_mem_dvsec);
+
 /**
  * __cxl_driver_register - register a driver for the cxl bus
  * @cxl_drv: cxl driver structure to attach
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index c9dd054bd813..0068b5ff5f3e 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
 {
 	unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
 }
+
+bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
+{
+	return !!cxlmd->dev.driver;
+}
+EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index b48bdbefd949..a168520d741b 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
 				   resource_size_t component_reg_phys,
 				   struct cxl_port *parent_port);
 
+bool is_cxl_port(struct device *dev);
 int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
 		  resource_size_t component_reg_phys);
+struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
 
 struct cxl_decoder *to_cxl_decoder(struct device *dev);
 bool is_root_decoder(struct device *dev);
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 811b24451604..88264204c4b9 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
 struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
 				       struct cxl_mem *cxlm);
 
+bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
+
 /**
  * struct cxl_mbox_cmd - A command to be submitted to hardware.
  * @opcode: (input) The command set and command submitted to hardware.
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 978a54b0a51a..b6dc34d18a86 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -2,8 +2,10 @@
 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/pci.h>
 
 #include "cxlmem.h"
+#include "pci.h"
 
 /**
  * DOC: cxl mem
@@ -17,9 +19,60 @@
  * components.
  */
 
+static int port_match(struct device *dev, const void *data)
+{
+	struct cxl_port *port;
+
+	if (!is_cxl_port(dev))
+		return 0;
+
+	port = to_cxl_port(dev);
+
+	if (find_dport_by_dev(port, (struct device *)data))
+		return 1;
+
+	return 0;
+}
+
+static bool is_cxl_mem_enabled(struct pci_dev *pdev)
+{
+	int pcie_dvsec;
+	u16 dvsec_ctrl;
+
+	pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
+	if (!pcie_dvsec) {
+		dev_info(&pdev->dev, "Unable to determine CXL protocol support");
+		return false;
+	}
+
+	pci_read_config_word(pdev,
+			     pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
+			     &dvsec_ctrl);
+	if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
+		dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
+		return false;
+	}
+
+	return true;
+}
+
 static int cxl_mem_probe(struct device *dev)
 {
-	return -EOPNOTSUPP;
+	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+	struct cxl_mem *cxlm = cxlmd->cxlm;
+	struct device *pdev_parent = cxlm->dev->parent;
+	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
+	struct device *port_dev;
+
+	if (!is_cxl_mem_enabled(pdev))
+		return -ENODEV;
+
+	/* TODO: if parent is a switch, this will fail. */
+	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
+	if (!port_dev)
+		return -ENODEV;
+
+	return 0;
 }
 
 static void cxl_mem_remove(struct device *dev)
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 6931885c83ce..244b99948c40 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -335,29 +335,6 @@ static void cxl_pci_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
 	pci_iounmap(to_pci_dev(cxlm->dev), base);
 }
 
-static int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
-{
-	int pos;
-
-	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
-	if (!pos)
-		return 0;
-
-	while (pos) {
-		u16 vendor, id;
-
-		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
-		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
-		if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
-			return pos;
-
-		pos = pci_find_next_ext_capability(pdev, pos,
-						   PCI_EXT_CAP_ID_DVSEC);
-	}
-
-	return 0;
-}
-
 static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
 			  struct cxl_register_map *map)
 {
diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
index 8c1a58813816..d6b9978d05b0 100644
--- a/drivers/cxl/pci.h
+++ b/drivers/cxl/pci.h
@@ -11,7 +11,10 @@
  */
 #define PCI_DVSEC_HEADER1_LENGTH_MASK	GENMASK(31, 20)
 #define PCI_DVSEC_VENDOR_ID_CXL		0x1E98
-#define PCI_DVSEC_ID_CXL		0x0
+
+#define PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID	0x0
+#define PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET	0xC
+#define   CXL_PCIE_MEM_ENABLE			BIT(2)
 
 #define PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID	0x8
 #define PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET	0xC
@@ -29,4 +32,6 @@
 
 #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
 
+int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
+
 #endif /* __CXL_PCI_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 08/13] cxl/mem: Add memdev as a port
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (6 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 07/13] cxl/memdev: Determine CXL.mem capability Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 15:31   ` Jonathan Cameron
  2021-09-10 23:09   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe Ben Widawsky
                   ` (5 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

CXL endpoints contain HDM decoders that are architecturally the same as
a CXL switch, or a CXL hostbridge. While some restrictions are in place
for endpoints, they will require the same enumeration logic to determine
the number and abilities of the HDM decoders.

Utilizing the existing port APIs from cxl_core is the simplest way to
gain access to the same set of information that switches and hostbridges
have.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c |  5 ++++-
 drivers/cxl/mem.c      | 10 +++++++++-
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 56f57302d27b..f26095b40f5c 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -377,7 +377,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
 
 	dev = &port->dev;
 	if (parent_port)
-		rc = dev_set_name(dev, "port%d", port->id);
+		if (host->type == &cxl_memdev_type)
+			rc = dev_set_name(dev, "devport%d", port->id);
+		else
+			rc = dev_set_name(dev, "port%d", port->id);
 	else
 		rc = dev_set_name(dev, "root%d", port->id);
 	if (rc)
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index b6dc34d18a86..9d5a3a29cda1 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -63,6 +63,7 @@ static int cxl_mem_probe(struct device *dev)
 	struct device *pdev_parent = cxlm->dev->parent;
 	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
 	struct device *port_dev;
+	int rc;
 
 	if (!is_cxl_mem_enabled(pdev))
 		return -ENODEV;
@@ -72,7 +73,14 @@ static int cxl_mem_probe(struct device *dev)
 	if (!port_dev)
 		return -ENODEV;
 
-	return 0;
+	/* TODO: Obtain component registers */
+	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
+					       CXL_RESOURCE_NONE,
+					       to_cxl_port(port_dev)));
+	if (rc)
+		dev_err(dev, "Unable to add devices upstream port");
+
+	return rc;
 }
 
 static void cxl_mem_remove(struct device *dev)
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (7 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 08/13] cxl/mem: Add memdev as a port Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-10 23:12   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

In order for a memdev to participate in cxl_core's port APIs, the
physical address of the memdev's component registers is needed. This is
accomplished by allocating the array of maps in probe so they can be
used after the memdev is created.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/pci.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 244b99948c40..e4b3549c4580 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -407,21 +407,22 @@ static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
 /**
  * cxl_pci_setup_regs() - Setup necessary MMIO.
  * @cxlm: The CXL memory device to communicate with.
+ * @maps: Array of maps populated by this function.
  *
- * Return: 0 if all necessary registers mapped.
+ * Return: 0 if all necessary registers mapped. The results are stored in @maps.
  *
  * A memory device is required by spec to implement a certain set of MMIO
  * regions. The purpose of this function is to enumerate and map those
  * registers.
  */
-static int cxl_pci_setup_regs(struct cxl_mem *cxlm)
+static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps[])
 {
 	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
 	struct device *dev = cxlm->dev;
 	u32 regloc_size, regblocks;
 	void __iomem *base;
 	int regloc, i, n_maps;
-	struct cxl_register_map *map, maps[CXL_REGLOC_RBI_TYPES];
+	struct cxl_register_map *map;
 	int ret = 0;
 
 	regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
@@ -492,6 +493,7 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm)
 
 static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+	struct cxl_register_map maps[CXL_REGLOC_RBI_TYPES];
 	struct cxl_memdev *cxlmd;
 	struct cxl_mem *cxlm;
 	int rc;
@@ -511,7 +513,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (IS_ERR(cxlm))
 		return PTR_ERR(cxlm);
 
-	rc = cxl_pci_setup_regs(cxlm);
+	rc = cxl_pci_setup_regs(cxlm, maps);
 	if (rc)
 		return rc;
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (8 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-02 22:41   ` Ben Widawsky
                     ` (2 more replies)
  2021-09-02 19:50 ` [PATCH 11/13] cxl/core: Convert decoder range to resource Ben Widawsky
                   ` (3 subsequent siblings)
  13 siblings, 3 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Component registers are implemented for CXL.mem/cache operations. The
cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
The driver for managing CXL.mem/cache operations will need the component
registers mapped and the mapping cannot be shared across two devices.

For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
exception (perhaps others will exist) where it might be desirable to
have the cxl_pci driver do negotiation. For this case, it probably will
make sense to create an ephemeral mapping. Further looking, there might
need to be a cxl_core mechanism to allow arbitrating access to the
component registers.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c    | 38 ++++++++++++++++++++++++++++++++++++++
 drivers/cxl/core/memdev.c | 11 +++++++----
 drivers/cxl/core/regs.c   |  6 +++---
 drivers/cxl/cxl.h         |  4 ++++
 drivers/cxl/cxlmem.h      |  4 +++-
 drivers/cxl/mem.c         |  3 +--
 drivers/cxl/pci.c         | 19 +++++++++++++++++--
 7 files changed, 73 insertions(+), 12 deletions(-)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index f26095b40f5c..01b6fa8373e4 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -310,6 +310,37 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
 	return devm_add_action_or_reset(host, cxl_unlink_uport, port);
 }
 
+static int cxl_port_map_component_registers(struct cxl_port *port)
+{
+	struct cxl_register_map map;
+	struct cxl_component_reg_map *comp_map = &map.component_map;
+	void __iomem *crb;
+
+	if (port->component_reg_phys == CXL_RESOURCE_NONE)
+		return 0;
+
+	crb = devm_cxl_iomap_block(&port->dev,
+				   port->component_reg_phys,
+				   /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);
+	if (IS_ERR(crb))
+		return PTR_ERR(crb);
+
+	if (!crb) {
+		dev_err(&port->dev, "No component registers mapped\n");
+		return -ENXIO;
+	}
+
+	cxl_probe_component_regs(&port->dev, crb, comp_map);
+	if (!comp_map->hdm_decoder.valid) {
+		dev_err(&port->dev, "HDM decoder registers invalid\n");
+		return -ENXIO;
+	}
+
+	port->regs.hdm_decoder = crb + comp_map->hdm_decoder.offset;
+
+	return 0;
+}
+
 static struct cxl_port *cxl_port_alloc(struct device *uport,
 				       resource_size_t component_reg_phys,
 				       struct cxl_port *parent_port)
@@ -398,6 +429,13 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
 	if (rc)
 		return ERR_PTR(rc);
 
+	/* Platform "switch" has no parent port or component registers */
+	if (parent_port) {
+		rc = cxl_port_map_component_registers(port);
+		if (rc)
+			return ERR_PTR(rc);
+	}
+
 	return port;
 
 err:
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 0068b5ff5f3e..85fe42abd29b 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -185,7 +185,8 @@ static void cxl_memdev_unregister(void *_cxlmd)
 }
 
 static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
-					   const struct file_operations *fops)
+					   const struct file_operations *fops,
+					   unsigned long component_reg_phys)
 {
 	struct cxl_memdev *cxlmd;
 	struct device *dev;
@@ -200,6 +201,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
 	if (rc < 0)
 		goto err;
 	cxlmd->id = rc;
+	cxlmd->component_reg_phys = component_reg_phys;
 
 	dev = &cxlmd->dev;
 	device_initialize(dev);
@@ -275,15 +277,16 @@ static const struct file_operations cxl_memdev_fops = {
 	.llseek = noop_llseek,
 };
 
-struct cxl_memdev *
-devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm)
+struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
+				       struct cxl_mem *cxlm,
+				       unsigned long component_reg_phys)
 {
 	struct cxl_memdev *cxlmd;
 	struct device *dev;
 	struct cdev *cdev;
 	int rc;
 
-	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
+	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops, component_reg_phys);
 	if (IS_ERR(cxlmd))
 		return cxlmd;
 
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 8535a7b94f28..4ba75fb6779f 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -145,9 +145,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
 }
 EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
 
-static void __iomem *devm_cxl_iomap_block(struct device *dev,
-					  resource_size_t addr,
-					  resource_size_t length)
+void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
+				   resource_size_t length)
 {
 	void __iomem *ret_val;
 	struct resource *res;
@@ -166,6 +165,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,
 
 	return ret_val;
 }
+EXPORT_SYMBOL_GPL(devm_cxl_iomap_block);
 
 int cxl_map_component_regs(struct pci_dev *pdev,
 			   struct cxl_component_regs *regs,
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index a168520d741b..4585d03a0a67 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -149,6 +149,8 @@ struct cxl_register_map {
 	};
 };
 
+void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
+				   resource_size_t length);
 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,
@@ -252,6 +254,7 @@ struct cxl_walk_context {
  * @dports: cxl_dport instances referenced by decoders
  * @decoder_ida: allocator for decoder ids
  * @component_reg_phys: component register capability base address (optional)
+ * @regs: Mapped version of @component_reg_phys
  */
 struct cxl_port {
 	struct device dev;
@@ -260,6 +263,7 @@ struct cxl_port {
 	struct list_head dports;
 	struct ida decoder_ida;
 	resource_size_t component_reg_phys;
+	struct cxl_component_regs regs;
 };
 
 /**
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 88264204c4b9..f94624e43b2e 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -41,6 +41,7 @@ struct cxl_memdev {
 	struct cdev cdev;
 	struct cxl_mem *cxlm;
 	int id;
+	unsigned long component_reg_phys;
 };
 
 static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
@@ -49,7 +50,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
 }
 
 struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
-				       struct cxl_mem *cxlm);
+				       struct cxl_mem *cxlm,
+				       unsigned long component_reg_phys);
 
 bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
 
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 9d5a3a29cda1..aba9a07d519f 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -73,9 +73,8 @@ static int cxl_mem_probe(struct device *dev)
 	if (!port_dev)
 		return -ENODEV;
 
-	/* TODO: Obtain component registers */
 	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
-					       CXL_RESOURCE_NONE,
+					       cxlmd->component_reg_phys,
 					       to_cxl_port(port_dev)));
 	if (rc)
 		dev_err(dev, "Unable to add devices upstream port");
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index e4b3549c4580..258190febb5a 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -382,8 +382,12 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
 
 	switch (map->reg_type) {
 	case CXL_REGLOC_RBI_COMPONENT:
+#ifndef CONFIG_CXL_MEM
 		cxl_map_component_regs(pdev, &cxlm->regs.component, map);
 		dev_dbg(dev, "Mapping component registers...\n");
+#else
+		dev_dbg(dev, "Component registers not mapped for %s\n", KBUILD_MODNAME);
+#endif
 		break;
 	case CXL_REGLOC_RBI_MEMDEV:
 		cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
@@ -493,10 +497,11 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps
 
 static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
 	struct cxl_register_map maps[CXL_REGLOC_RBI_TYPES];
 	struct cxl_memdev *cxlmd;
 	struct cxl_mem *cxlm;
-	int rc;
+	int rc, i;
 
 	/*
 	 * Double check the anonymous union trickery in struct cxl_regs
@@ -533,7 +538,17 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (rc)
 		return rc;
 
-	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm);
+	for (i = 0; i < ARRAY_SIZE(maps); i++) {
+		struct cxl_register_map *map = &maps[i];
+
+		if (map->reg_type != CXL_REGLOC_RBI_COMPONENT)
+			continue;
+
+		component_reg_phys = pci_resource_start(pdev, map->barno) +
+				     map->block_offset;
+	}
+
+	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, component_reg_phys);
 	if (IS_ERR(cxlmd))
 		return PTR_ERR(cxlmd);
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 11/13] cxl/core: Convert decoder range to resource
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (9 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 16:16   ` Jonathan Cameron
  2021-09-11  0:59   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders Ben Widawsky
                   ` (2 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Regions will use the resource API in order to help manage allocated
space. As regions are children of the decoder, it makes sense that the
parent host the main resource to be suballocated by the region.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/acpi.c     | 12 ++++--------
 drivers/cxl/core/bus.c |  4 ++--
 drivers/cxl/cxl.h      |  4 ++--
 3 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index fd14094bdb3f..26691313d716 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -125,10 +125,9 @@ static void cxl_add_cfmws_decoders(struct device *dev,
 
 		cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
 		cxld->target_type = CXL_DECODER_EXPANDER;
-		cxld->range = (struct range) {
-			.start = cfmws->base_hpa,
-			.end = cfmws->base_hpa + cfmws->window_size - 1,
-		};
+		cxld->res = (struct resource)DEFINE_RES_MEM_NAMED(cfmws->base_hpa,
+								  cfmws->window_size,
+								  "cfmws");
 		cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
 		cxld->interleave_granularity =
 			CFMWS_INTERLEAVE_GRANULARITY(cfmws);
@@ -318,10 +317,7 @@ static int add_host_bridge_uport(struct device *match, void *arg)
 	cxld->interleave_ways = 1;
 	cxld->interleave_granularity = PAGE_SIZE;
 	cxld->target_type = CXL_DECODER_EXPANDER;
-	cxld->range = (struct range) {
-		.start = 0,
-		.end = -1,
-	};
+	cxld->res = (struct resource)DEFINE_RES_MEM(0, 0);
 
 	device_lock(&port->dev);
 	dport = list_first_entry(&port->dports, typeof(*dport), list);
diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 01b6fa8373e4..d056dbd794a4 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -48,7 +48,7 @@ static ssize_t start_show(struct device *dev, struct device_attribute *attr,
 {
 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
 
-	return sysfs_emit(buf, "%#llx\n", cxld->range.start);
+	return sysfs_emit(buf, "%#llx\n", cxld->res.start);
 }
 static DEVICE_ATTR_RO(start);
 
@@ -57,7 +57,7 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
 {
 	struct cxl_decoder *cxld = to_cxl_decoder(dev);
 
-	return sysfs_emit(buf, "%#llx\n", range_len(&cxld->range));
+	return sysfs_emit(buf, "%#llx\n", resource_size(&cxld->res));
 }
 static DEVICE_ATTR_RO(size);
 
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 4585d03a0a67..e610fa9dd6c8 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -192,7 +192,7 @@ enum cxl_decoder_type {
  * struct cxl_decoder - CXL address range decode configuration
  * @dev: this decoder's device
  * @id: kernel device name id
- * @range: address range considered by this decoder
+ * @res: address space resources considered by this decoder
  * @interleave_ways: number of cxl_dports in this decode
  * @interleave_granularity: data stride per dport
  * @target_type: accelerator vs expander (type2 vs type3) selector
@@ -203,7 +203,7 @@ enum cxl_decoder_type {
 struct cxl_decoder {
 	struct device dev;
 	int id;
-	struct range range;
+	struct resource res;
 	int interleave_ways;
 	int interleave_granularity;
 	enum cxl_decoder_type target_type;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (10 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 11/13] cxl/core: Convert decoder range to resource Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 17:43   ` Jonathan Cameron
  2021-09-11  1:13   ` Dan Williams
  2021-09-02 19:50 ` [PATCH 13/13] cxl/mem: Enumerate switch decoders Ben Widawsky
  2021-09-10 18:15 ` [PATCH 00/13] Enumerate midlevel and endpoint decoders Dan Williams
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

As of the CXL 2.0 specification, every port will have between 1 and 10
HDM decoders available in hardware. These exist in the endpoint, switch,
and top level hostbridges. HDM decoders are required for configuration
CXL regions, and therefore enumerating them is an important first step.

As an example, the below has 4 decoders, a top level CFMWS decoder
(0.0), a single decoder in a single host bridge (1.0), and two devices
each with 1 decoder (2.0 and 3.0)

├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
├── decoder1.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/decoder1.0
├── decoder2.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport2/decoder2.0
├── decoder3.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport3/decoder3.0

Additionally, attributes are added for a port:

/sys/bus/cxl/devices/port1
├── active_decoders
├── decoder_count
├── decoder_enabled
├── max_target_count
...

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/core/bus.c | 161 ++++++++++++++++++++++++++++++++++++++++-
 drivers/cxl/cxl.h      |  54 ++++++++++++--
 2 files changed, 209 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index d056dbd794a4..b75e42965e89 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -43,6 +43,15 @@ struct attribute_group cxl_base_attribute_group = {
 	.attrs = cxl_base_attributes,
 };
 
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct cxl_decoder *cxld = to_cxl_decoder(dev);
+
+	return sysfs_emit(buf, "%d\n", !!cxld->decoder_enabled);
+}
+static DEVICE_ATTR_RO(enabled);
+
 static ssize_t start_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
@@ -130,6 +139,7 @@ static ssize_t target_list_show(struct device *dev,
 static DEVICE_ATTR_RO(target_list);
 
 static struct attribute *cxl_decoder_base_attrs[] = {
+	&dev_attr_enabled.attr,
 	&dev_attr_start.attr,
 	&dev_attr_size.attr,
 	&dev_attr_locked.attr,
@@ -249,8 +259,48 @@ static void cxl_port_release(struct device *dev)
 	kfree(port);
 }
 
+static ssize_t active_decoders_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct cxl_port *port = to_cxl_port(dev);
+
+	return sysfs_emit(buf, "%*pbl\n", port->decoder_cap.count,
+			  port->used_decoders);
+}
+static DEVICE_ATTR_RO(active_decoders);
+
+static ssize_t decoder_count_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct cxl_port *port = to_cxl_port(dev);
+
+	return sysfs_emit(buf, "%d\n", port->decoder_cap.count);
+}
+static DEVICE_ATTR_RO(decoder_count);
+
+static ssize_t max_target_count_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct cxl_port *port = to_cxl_port(dev);
+
+	return sysfs_emit(buf, "%d\n", port->decoder_cap.target_count);
+}
+static DEVICE_ATTR_RO(max_target_count);
+
+static struct attribute *cxl_port_caps_attributes[] = {
+	&dev_attr_active_decoders.attr,
+	&dev_attr_decoder_count.attr,
+	&dev_attr_max_target_count.attr,
+	NULL,
+};
+
+struct attribute_group cxl_port_attribute_group = {
+	.attrs = cxl_port_caps_attributes,
+};
+
 static const struct attribute_group *cxl_port_attribute_groups[] = {
 	&cxl_base_attribute_group,
+	&cxl_port_attribute_group,
 	NULL,
 };
 
@@ -341,6 +391,107 @@ static int cxl_port_map_component_registers(struct cxl_port *port)
 	return 0;
 }
 
+static int port_populate_caps(struct cxl_port *port)
+{
+	void __iomem *hdm_decoder = port->regs.hdm_decoder;
+	u32 hdm_cap;
+
+	hdm_cap = readl(hdm_decoder + CXL_HDM_DECODER_CAP_OFFSET);
+
+	port->used_decoders = devm_bitmap_zalloc(&port->dev,
+						 cxl_hdm_decoder_count(hdm_cap),
+						 GFP_KERNEL);
+	if (!port->used_decoders)
+		return -ENOMEM;
+
+	port->decoder_cap.count = cxl_hdm_decoder_count(hdm_cap);
+	port->decoder_cap.target_count =
+		FIELD_GET(CXL_HDM_DECODER_TARGET_COUNT_MASK, hdm_cap);
+	port->decoder_cap.interleave11_8 =
+		FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_11_8, hdm_cap);
+	port->decoder_cap.interleave14_12 =
+		FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap);
+
+	return 0;
+}
+
+static int cxl_port_enumerate_hdm_decoders(struct device *host,
+					   struct cxl_port *port)
+{
+	void __iomem *hdm_decoder = port->regs.hdm_decoder;
+	u32 hdm_ctrl;
+	int i, rc = 0;
+
+	rc = port_populate_caps(port);
+	if (rc)
+		return rc;
+
+	if (port->decoder_cap.count == 0) {
+		dev_warn(host, "Found no HDM decoders\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < port->decoder_cap.count; i++) {
+		enum cxl_decoder_type type = CXL_DECODER_EXPANDER;
+		struct resource res = DEFINE_RES_MEM(0, 0);
+		struct cxl_decoder *cxld;
+		int iw = 0, ig = 0;
+		u32 ctrl;
+
+		cxld = cxl_decoder_alloc(port, is_endpoint_decoder(host) ? 0 :
+					 port->decoder_cap.target_count);
+		if (IS_ERR(cxld)) {
+			dev_warn(host, "Failed to allocate the decoder\n");
+			return PTR_ERR(cxld);
+		}
+
+		ctrl = readl(hdm_decoder + CXL_HDM_DECODER0_CTRL_OFFSET(i));
+		cxld->decoder_enabled =
+			!!FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl);
+		/* If the decoder is already active, parse info */
+		if (cxld->decoder_enabled) {
+			set_bit(i, port->used_decoders);
+			iw = cxl_hdm_decoder_iw(ctrl);
+			ig = cxl_hdm_decoder_ig(ctrl);
+			if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0)
+				type = CXL_DECODER_ACCELERATOR;
+			res.start = readl(hdm_decoder +
+					  CXL_HDM_DECODER0_BASE_LOW_OFFSET(i));
+			res.start |=
+				(u64)readl(hdm_decoder +
+					   CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i))
+				<< 32;
+		}
+
+		cxld->target_type = type;
+		cxld->res = res;
+		cxld->interleave_ways = iw;
+		cxld->interleave_granularity = ig;
+
+		rc = cxl_decoder_add(host, cxld, NULL);
+		if (rc) {
+			dev_warn(host, "Failed to add decoder (%d)\n", rc);
+			kfree(cxld);
+			goto out;
+		}
+	}
+
+	/*
+	 * Enable CXL.mem decoding via MMIO for endpoint devices
+	 *
+	 * TODO: If a memory device was configured to participate in a region by
+	 * system firmware via DVSEC, this will break that region.
+	 */
+	if (is_endpoint_decoder(host)) {
+		hdm_ctrl = readl(hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
+		writel(hdm_ctrl | CXL_HDM_DECODER_ENABLE,
+		       hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
+	}
+
+out:
+	return rc;
+}
+
 static struct cxl_port *cxl_port_alloc(struct device *uport,
 				       resource_size_t component_reg_phys,
 				       struct cxl_port *parent_port)
@@ -432,8 +583,16 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
 	/* Platform "switch" has no parent port or component registers */
 	if (parent_port) {
 		rc = cxl_port_map_component_registers(port);
-		if (rc)
+		if (rc) {
+			dev_err(host, "Failed to map component registers\n");
 			return ERR_PTR(rc);
+		}
+
+		rc = cxl_port_enumerate_hdm_decoders(host, port);
+		if (rc) {
+			dev_err(host, "Failed to enumerate HDM decoders\n");
+			return ERR_PTR(rc);
+		}
 	}
 
 	return port;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index e610fa9dd6c8..6759fe097e12 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -36,11 +36,19 @@
 #define CXL_HDM_DECODER_CAP_OFFSET 0x0
 #define   CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
 #define   CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
-#define CXL_HDM_DECODER0_BASE_LOW_OFFSET 0x10
-#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET 0x14
-#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET 0x18
-#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET 0x1c
-#define CXL_HDM_DECODER0_CTRL_OFFSET 0x20
+#define   CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
+#define   CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
+#define CXL_HDM_DECODER_CTRL_OFFSET 0x0
+#define   CXL_HDM_DECODER_ENABLE BIT(1)
+#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x10 + (i) * 0x20)
+#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i) (0x14 + (i) * 0x20)
+#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i) (0x18 + (i) * 0x20)
+#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i) (0x1c + (i) * 0x20)
+#define CXL_HDM_DECODER0_CTRL_OFFSET(i) (0x20 + (i) * 0x20)
+#define   CXL_HDM_DECODER0_CTRL_IG_MASK GENMASK(3, 0)
+#define   CXL_HDM_DECODER0_CTRL_IW_MASK GENMASK(7, 4)
+#define   CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
+#define   CXL_HDM_DECODER0_CTRL_TYPE BIT(12)
 
 static inline int cxl_hdm_decoder_count(u32 cap_hdr)
 {
@@ -49,6 +57,20 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr)
 	return val ? val * 2 : 1;
 }
 
+static inline int cxl_hdm_decoder_ig(u32 ctrl)
+{
+	int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl);
+
+	return 8 + val;
+}
+
+static inline int cxl_hdm_decoder_iw(u32 ctrl)
+{
+	int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl);
+
+	return 1 << val;
+}
+
 /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
 #define CXLDEV_CAP_ARRAY_OFFSET 0x0
 #define   CXLDEV_CAP_ARRAY_CAP_ID 0
@@ -188,6 +210,12 @@ enum cxl_decoder_type {
  */
 #define CXL_DECODER_MAX_INTERLEAVE 16
 
+/*
+ * Current specification goes up to 10 double that seems a reasonable
+ * software max for the foreseeable future
+ */
+#define CXL_DECODER_MAX_COUNT 20
+
 /**
  * struct cxl_decoder - CXL address range decode configuration
  * @dev: this decoder's device
@@ -197,6 +225,7 @@ enum cxl_decoder_type {
  * @interleave_granularity: data stride per dport
  * @target_type: accelerator vs expander (type2 vs type3) selector
  * @flags: memory type capabilities and locking
+ * @decoder_enabled: Is this decoder currently decoding
  * @nr_targets: number of elements in @target
  * @target: active ordered target list in current decoder configuration
  */
@@ -208,6 +237,7 @@ struct cxl_decoder {
 	int interleave_granularity;
 	enum cxl_decoder_type target_type;
 	unsigned long flags;
+	bool decoder_enabled;
 	int nr_targets;
 	struct cxl_dport *target[];
 };
@@ -255,6 +285,12 @@ struct cxl_walk_context {
  * @decoder_ida: allocator for decoder ids
  * @component_reg_phys: component register capability base address (optional)
  * @regs: Mapped version of @component_reg_phys
+ * @used_decoders: Bitmap of currently active decoders for the port
+ * @decoder_cap: Capabilities of all decoders contained by the port
+ * @decoder_cap.count: Count of HDM decoders for the port
+ * @decoder_cap.target_count: Max number of interleaved downstream ports
+ * @decoder_cap.interleave11_8: Are address bits 11-8 available for interleave
+ * @decoder_cap.interleave14_12: Are address bits 14-12 available for interleave
  */
 struct cxl_port {
 	struct device dev;
@@ -264,6 +300,14 @@ struct cxl_port {
 	struct ida decoder_ida;
 	resource_size_t component_reg_phys;
 	struct cxl_component_regs regs;
+
+	unsigned long *used_decoders;
+	struct {
+		int count;
+		int target_count;
+		bool interleave11_8;
+		bool interleave14_12;
+	} decoder_cap;
 };
 
 /**
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH 13/13] cxl/mem: Enumerate switch decoders
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (11 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders Ben Widawsky
@ 2021-09-02 19:50 ` Ben Widawsky
  2021-09-03 17:56   ` Jonathan Cameron
  2021-09-14 23:31   ` Dan Williams
  2021-09-10 18:15 ` [PATCH 00/13] Enumerate midlevel and endpoint decoders Dan Williams
  13 siblings, 2 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 19:50 UTC (permalink / raw)
  To: linux-cxl
  Cc: Ben Widawsky, Alison Schofield, Dan Williams, Ira Weiny,
	Jonathan Cameron, Vishal Verma

Switches work much in the same way as hostbridges. The primary
difference is that they are enumerated, and probed via regular PCIe
mechanisms. A switch has 1 upstream port, and n downstream ports.
Ultimately a memory device attached to a switch can determine if it's in
a CXL capable subset of the topology if the switch is CXL capable.

The algorithm introduced enables enumerating switches in a CXL topology.
It walks up the topology until it finds a root port (which is enumerated
by the cxl_acpi driver). Once at the top, it walks back down adding all
downstream ports along the way.

Note that practically speaking there can be at most 3 levels of switches
with the current 2.0 spec. This is because there is a max interleave of
8 defined in the spec. If there is a single hostbridge and only 1 root
port was CXL capable, you could have 3 levels of x2 switches, making
the x8 interleave. However, as far as the spec is concerned, there can
be infinite number of switches since a x1 switch is allowed, and
future versions of the spec may allow for a larger total interleave.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 drivers/cxl/mem.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-
 drivers/cxl/pci.c |   8 ---
 drivers/cxl/pci.h |   8 +++
 3 files changed, 137 insertions(+), 9 deletions(-)

diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index aba9a07d519f..dc8ca43d5bfc 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -56,6 +56,133 @@ static bool is_cxl_mem_enabled(struct pci_dev *pdev)
 	return true;
 }
 
+/* TODO: dedeuplicate this from drivers/cxl/pci.c? */
+static unsigned long get_component_regs(struct pci_dev *pdev)
+{
+	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
+	u32 regloc_size, regblocks;
+	int regloc, i;
+
+	regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
+	if (!regloc) {
+		dev_err(&pdev->dev, "register location dvsec not found\n");
+		return component_reg_phys;
+	}
+
+	/* Get the size of the Register Locator DVSEC */
+	pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
+	regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
+
+	regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
+	regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
+
+	for (i = 0; i < regblocks; i++, regloc += 8) {
+		u32 reg_lo, reg_hi;
+		u8 reg_type;
+		u64 offset;
+		u8 bar;
+
+		pci_read_config_dword(pdev, regloc, &reg_lo);
+		pci_read_config_dword(pdev, regloc + 4, &reg_hi);
+
+		cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,
+					  &reg_type);
+
+		if (reg_type != CXL_REGLOC_RBI_COMPONENT)
+			continue;
+
+		component_reg_phys = pci_resource_start(pdev, bar) + offset;
+	}
+
+	return component_reg_phys;
+}
+
+static void enumerate_uport(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	/*
+	 * Parent's parent should be another uport, since we don't have root
+	 * ports here
+	 */
+	if (dev_WARN_ONCE(dev, !dev->parent->parent, "No grandparent port\n"))
+		return;
+
+	if (!is_cxl_port(dev->parent->parent)) {
+		dev_info(dev, "Parent of uport isn't a CXL port (%s)\n",
+			 dev_name(dev->parent->parent));
+		return;
+	}
+
+	devm_cxl_add_port(dev, dev, get_component_regs(pdev),
+			  to_cxl_port(dev->parent));
+}
+
+static void enumerate_dport(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	u32 port_num, lnkcap;
+
+	if (dev_WARN_ONCE(dev, !dev->parent, "No parent port\n"))
+		return;
+
+	if (!is_cxl_port(dev->parent)) {
+		dev_info(dev, "Uport isn't a CXL port %s\n",
+			 dev_name(dev->parent));
+		return;
+	}
+
+	/* TODO: deduplicate from drivers/cxl/acpi.c? */
+	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
+				  &lnkcap) != PCIBIOS_SUCCESSFUL)
+		return;
+	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
+
+	cxl_add_dport(to_cxl_port(dev->parent), dev, port_num,
+		      get_component_regs(pdev));
+}
+
+/*
+ * Walk up the topology until we get to the root port (ie. parent is a
+ * cxl port). From there walk back down adding the additional ports. If the
+ * parent isn't a PCIe switch (upstream or downstream port), the downstream
+ * endpoint(s) cannot be CXL enabled.
+ *
+ * XXX: It's possible that cxl_acpi hasn't yet enumerated the root ports, and
+ * so that will rescan the CXL bus, thus coming back here.
+ */
+static void enumerate_switches(struct device *dev)
+{
+	struct pci_dev *pdev;
+	int type;
+
+	if (unlikely(!dev))
+		return;
+
+	if (unlikely(!dev_is_pci(dev)))
+		return;
+
+	pdev = to_pci_dev(dev);
+
+	if (unlikely(!pci_is_pcie(pdev)))
+		return;
+
+	if (!is_cxl_mem_enabled(pdev))
+		return;
+
+	type = pci_pcie_type(pdev);
+
+	if (type != PCI_EXP_TYPE_UPSTREAM && type != PCI_EXP_TYPE_DOWNSTREAM)
+		return;
+
+	enumerate_switches(dev->parent);
+
+	if (type == PCI_EXP_TYPE_UPSTREAM)
+		enumerate_uport(dev);
+	if (type == PCI_EXP_TYPE_DOWNSTREAM)
+		enumerate_dport(dev);
+}
+
 static int cxl_mem_probe(struct device *dev)
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
@@ -68,7 +195,8 @@ static int cxl_mem_probe(struct device *dev)
 	if (!is_cxl_mem_enabled(pdev))
 		return -ENODEV;
 
-	/* TODO: if parent is a switch, this will fail. */
+	enumerate_switches(dev->parent);
+
 	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
 	if (!port_dev)
 		return -ENODEV;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 258190febb5a..e338f2f759d0 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -400,14 +400,6 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
 	return 0;
 }
 
-static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
-				      u8 *bar, u64 *offset, u8 *reg_type)
-{
-	*offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
-	*bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
-	*reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
-}
-
 /**
  * cxl_pci_setup_regs() - Setup necessary MMIO.
  * @cxlm: The CXL memory device to communicate with.
diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
index d6b9978d05b0..8250d487e39d 100644
--- a/drivers/cxl/pci.h
+++ b/drivers/cxl/pci.h
@@ -34,4 +34,12 @@
 
 int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
 
+static inline void cxl_decode_register_block(u32 reg_lo, u32 reg_hi, u8 *bar,
+					     u64 *offset, u8 *reg_type)
+{
+	*offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
+	*bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
+	*reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
+}
+
 #endif /* __CXL_PCI_H__ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
@ 2021-09-02 22:41   ` Ben Widawsky
  2021-09-02 22:42     ` Ben Widawsky
  2021-09-03 16:14   ` Jonathan Cameron
  2021-09-10 23:44   ` Dan Williams
  2 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 22:41 UTC (permalink / raw)
  To: linux-cxl
  Cc: Alison Schofield, Dan Williams, Ira Weiny, Jonathan Cameron,
	Vishal Verma

On 21-09-02 12:50:14, Ben Widawsky wrote:
> Component registers are implemented for CXL.mem/cache operations. The
> cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> The driver for managing CXL.mem/cache operations will need the component
> registers mapped and the mapping cannot be shared across two devices.
> 
> For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> exception (perhaps others will exist) where it might be desirable to
> have the cxl_pci driver do negotiation. For this case, it probably will
> make sense to create an ephemeral mapping. Further looking, there might
> need to be a cxl_core mechanism to allow arbitrating access to the
> component registers.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c    | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/memdev.c | 11 +++++++----
>  drivers/cxl/core/regs.c   |  6 +++---
>  drivers/cxl/cxl.h         |  4 ++++
>  drivers/cxl/cxlmem.h      |  4 +++-
>  drivers/cxl/mem.c         |  3 +--
>  drivers/cxl/pci.c         | 19 +++++++++++++++++--
>  7 files changed, 73 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index f26095b40f5c..01b6fa8373e4 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -310,6 +310,37 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
>  	return devm_add_action_or_reset(host, cxl_unlink_uport, port);
>  }
>  
> +static int cxl_port_map_component_registers(struct cxl_port *port)
> +{
> +	struct cxl_register_map map;
> +	struct cxl_component_reg_map *comp_map = &map.component_map;
> +	void __iomem *crb;
> +
> +	if (port->component_reg_phys == CXL_RESOURCE_NONE)
> +		return 0;
> +
> +	crb = devm_cxl_iomap_block(&port->dev,
> +				   port->component_reg_phys,
> +				   /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);

I meant to fix this before sending...

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index 55c8440dfe00..0243d9a7fe3a 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -454,7 +454,7 @@ static int cxl_port_map_component_registers(struct cxl_port *port)

        crb = devm_cxl_iomap_block(&port->dev,
                                   port->component_reg_phys,
-                                  /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);
+                                  CXL_COMPONENT_REG_BLOCK_SIZE);
        if (IS_ERR(crb))
                return PTR_ERR(crb);

diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index e06b1f7d1419..dbbda32ca055 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -17,8 +17,12 @@
  * (port-driver, region-driver, nvdimm object-drivers... etc).
  */

+/* CXL 2.0 8.2.4 Table 141 Component Register Layout and Definition */
+#define CXL_COMPONENT_REG_BLOCK_SIZE SZ_64K
+
 /* CXL 2.0 8.2.5 CXL.cache and CXL.mem Registers*/
 #define CXL_CM_OFFSET 0x1000
+#define CXL_CM_SIZE SZ_64K
 #define CXL_CM_CAP_HDR_OFFSET 0x0
 #define   CXL_CM_CAP_HDR_ID_MASK GENMASK(15, 0)
 #define     CM_CAP_HDR_CAP_ID 1

> +	if (IS_ERR(crb))
> +		return PTR_ERR(crb);
> +
> +	if (!crb) {
> +		dev_err(&port->dev, "No component registers mapped\n");
> +		return -ENXIO;
> +	}
> +
> +	cxl_probe_component_regs(&port->dev, crb, comp_map);
> +	if (!comp_map->hdm_decoder.valid) {
> +		dev_err(&port->dev, "HDM decoder registers invalid\n");
> +		return -ENXIO;
> +	}
> +
> +	port->regs.hdm_decoder = crb + comp_map->hdm_decoder.offset;
> +
> +	return 0;
> +}
> +
>  static struct cxl_port *cxl_port_alloc(struct device *uport,
>  				       resource_size_t component_reg_phys,
>  				       struct cxl_port *parent_port)
> @@ -398,6 +429,13 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>  	if (rc)
>  		return ERR_PTR(rc);
>  
> +	/* Platform "switch" has no parent port or component registers */
> +	if (parent_port) {
> +		rc = cxl_port_map_component_registers(port);
> +		if (rc)
> +			return ERR_PTR(rc);
> +	}
> +
>  	return port;
>  
>  err:
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 0068b5ff5f3e..85fe42abd29b 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -185,7 +185,8 @@ static void cxl_memdev_unregister(void *_cxlmd)
>  }
>  
>  static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
> -					   const struct file_operations *fops)
> +					   const struct file_operations *fops,
> +					   unsigned long component_reg_phys)
>  {
>  	struct cxl_memdev *cxlmd;
>  	struct device *dev;
> @@ -200,6 +201,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
>  	if (rc < 0)
>  		goto err;
>  	cxlmd->id = rc;
> +	cxlmd->component_reg_phys = component_reg_phys;
>  
>  	dev = &cxlmd->dev;
>  	device_initialize(dev);
> @@ -275,15 +277,16 @@ static const struct file_operations cxl_memdev_fops = {
>  	.llseek = noop_llseek,
>  };
>  
> -struct cxl_memdev *
> -devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm)
> +struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> +				       struct cxl_mem *cxlm,
> +				       unsigned long component_reg_phys)
>  {
>  	struct cxl_memdev *cxlmd;
>  	struct device *dev;
>  	struct cdev *cdev;
>  	int rc;
>  
> -	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
> +	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops, component_reg_phys);
>  	if (IS_ERR(cxlmd))
>  		return cxlmd;
>  
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 8535a7b94f28..4ba75fb6779f 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -145,9 +145,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
>  }
>  EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
>  
> -static void __iomem *devm_cxl_iomap_block(struct device *dev,
> -					  resource_size_t addr,
> -					  resource_size_t length)
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +				   resource_size_t length)
>  {
>  	void __iomem *ret_val;
>  	struct resource *res;
> @@ -166,6 +165,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,
>  
>  	return ret_val;
>  }
> +EXPORT_SYMBOL_GPL(devm_cxl_iomap_block);
>  
>  int cxl_map_component_regs(struct pci_dev *pdev,
>  			   struct cxl_component_regs *regs,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a168520d741b..4585d03a0a67 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -149,6 +149,8 @@ struct cxl_register_map {
>  	};
>  };
>  
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +				   resource_size_t length);
>  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,
> @@ -252,6 +254,7 @@ struct cxl_walk_context {
>   * @dports: cxl_dport instances referenced by decoders
>   * @decoder_ida: allocator for decoder ids
>   * @component_reg_phys: component register capability base address (optional)
> + * @regs: Mapped version of @component_reg_phys
>   */
>  struct cxl_port {
>  	struct device dev;
> @@ -260,6 +263,7 @@ struct cxl_port {
>  	struct list_head dports;
>  	struct ida decoder_ida;
>  	resource_size_t component_reg_phys;
> +	struct cxl_component_regs regs;
>  };
>  
>  /**
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 88264204c4b9..f94624e43b2e 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -41,6 +41,7 @@ struct cxl_memdev {
>  	struct cdev cdev;
>  	struct cxl_mem *cxlm;
>  	int id;
> +	unsigned long component_reg_phys;
>  };
>  
>  static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> @@ -49,7 +50,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  }
>  
>  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> -				       struct cxl_mem *cxlm);
> +				       struct cxl_mem *cxlm,
> +				       unsigned long component_reg_phys);
>  
>  bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
>  
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 9d5a3a29cda1..aba9a07d519f 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -73,9 +73,8 @@ static int cxl_mem_probe(struct device *dev)
>  	if (!port_dev)
>  		return -ENODEV;
>  
> -	/* TODO: Obtain component registers */
>  	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
> -					       CXL_RESOURCE_NONE,
> +					       cxlmd->component_reg_phys,
>  					       to_cxl_port(port_dev)));
>  	if (rc)
>  		dev_err(dev, "Unable to add devices upstream port");
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index e4b3549c4580..258190febb5a 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -382,8 +382,12 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
>  
>  	switch (map->reg_type) {
>  	case CXL_REGLOC_RBI_COMPONENT:
> +#ifndef CONFIG_CXL_MEM
>  		cxl_map_component_regs(pdev, &cxlm->regs.component, map);
>  		dev_dbg(dev, "Mapping component registers...\n");
> +#else
> +		dev_dbg(dev, "Component registers not mapped for %s\n", KBUILD_MODNAME);
> +#endif
>  		break;
>  	case CXL_REGLOC_RBI_MEMDEV:
>  		cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
> @@ -493,10 +497,11 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps
>  
>  static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
> +	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
>  	struct cxl_register_map maps[CXL_REGLOC_RBI_TYPES];
>  	struct cxl_memdev *cxlmd;
>  	struct cxl_mem *cxlm;
> -	int rc;
> +	int rc, i;
>  
>  	/*
>  	 * Double check the anonymous union trickery in struct cxl_regs
> @@ -533,7 +538,17 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  	if (rc)
>  		return rc;
>  
> -	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm);
> +	for (i = 0; i < ARRAY_SIZE(maps); i++) {
> +		struct cxl_register_map *map = &maps[i];
> +
> +		if (map->reg_type != CXL_REGLOC_RBI_COMPONENT)
> +			continue;
> +
> +		component_reg_phys = pci_resource_start(pdev, map->barno) +
> +				     map->block_offset;
> +	}
> +
> +	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, component_reg_phys);
>  	if (IS_ERR(cxlmd))
>  		return PTR_ERR(cxlmd);
>  
> -- 
> 2.33.0
> 

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-02 22:41   ` Ben Widawsky
@ 2021-09-02 22:42     ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-02 22:42 UTC (permalink / raw)
  To: linux-cxl
  Cc: Alison Schofield, Dan Williams, Ira Weiny, Jonathan Cameron,
	Vishal Verma

On 21-09-02 15:41:12, Ben Widawsky wrote:
> On 21-09-02 12:50:14, Ben Widawsky wrote:
> > Component registers are implemented for CXL.mem/cache operations. The
> > cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> > The driver for managing CXL.mem/cache operations will need the component
> > registers mapped and the mapping cannot be shared across two devices.
> > 
> > For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> > exception (perhaps others will exist) where it might be desirable to
> > have the cxl_pci driver do negotiation. For this case, it probably will
> > make sense to create an ephemeral mapping. Further looking, there might
> > need to be a cxl_core mechanism to allow arbitrating access to the
> > component registers.
> > 
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/core/bus.c    | 38 ++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/core/memdev.c | 11 +++++++----
> >  drivers/cxl/core/regs.c   |  6 +++---
> >  drivers/cxl/cxl.h         |  4 ++++
> >  drivers/cxl/cxlmem.h      |  4 +++-
> >  drivers/cxl/mem.c         |  3 +--
> >  drivers/cxl/pci.c         | 19 +++++++++++++++++--
> >  7 files changed, 73 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index f26095b40f5c..01b6fa8373e4 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -310,6 +310,37 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
> >  	return devm_add_action_or_reset(host, cxl_unlink_uport, port);
> >  }
> >  
> > +static int cxl_port_map_component_registers(struct cxl_port *port)
> > +{
> > +	struct cxl_register_map map;
> > +	struct cxl_component_reg_map *comp_map = &map.component_map;
> > +	void __iomem *crb;
> > +
> > +	if (port->component_reg_phys == CXL_RESOURCE_NONE)
> > +		return 0;
> > +
> > +	crb = devm_cxl_iomap_block(&port->dev,
> > +				   port->component_reg_phys,
> > +				   /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);
> 
> I meant to fix this before sending...
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 55c8440dfe00..0243d9a7fe3a 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -454,7 +454,7 @@ static int cxl_port_map_component_registers(struct cxl_port *port)
> 
>         crb = devm_cxl_iomap_block(&port->dev,
>                                    port->component_reg_phys,
> -                                  /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);
> +                                  CXL_COMPONENT_REG_BLOCK_SIZE);
>         if (IS_ERR(crb))
>                 return PTR_ERR(crb);
> 
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index e06b1f7d1419..dbbda32ca055 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -17,8 +17,12 @@
>   * (port-driver, region-driver, nvdimm object-drivers... etc).
>   */
> 
> +/* CXL 2.0 8.2.4 Table 141 Component Register Layout and Definition */
> +#define CXL_COMPONENT_REG_BLOCK_SIZE SZ_64K
> +
>  /* CXL 2.0 8.2.5 CXL.cache and CXL.mem Registers*/
>  #define CXL_CM_OFFSET 0x1000
> +#define CXL_CM_SIZE SZ_64K

doh. Ignore.

>  #define CXL_CM_CAP_HDR_OFFSET 0x0
>  #define   CXL_CM_CAP_HDR_ID_MASK GENMASK(15, 0)
>  #define     CM_CAP_HDR_CAP_ID 1
> 
> > +	if (IS_ERR(crb))
> > +		return PTR_ERR(crb);
> > +
> > +	if (!crb) {
> > +		dev_err(&port->dev, "No component registers mapped\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	cxl_probe_component_regs(&port->dev, crb, comp_map);
> > +	if (!comp_map->hdm_decoder.valid) {
> > +		dev_err(&port->dev, "HDM decoder registers invalid\n");
> > +		return -ENXIO;
> > +	}
> > +
> > +	port->regs.hdm_decoder = crb + comp_map->hdm_decoder.offset;
> > +
> > +	return 0;
> > +}
> > +
> >  static struct cxl_port *cxl_port_alloc(struct device *uport,
> >  				       resource_size_t component_reg_phys,
> >  				       struct cxl_port *parent_port)
> > @@ -398,6 +429,13 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> >  	if (rc)
> >  		return ERR_PTR(rc);
> >  
> > +	/* Platform "switch" has no parent port or component registers */
> > +	if (parent_port) {
> > +		rc = cxl_port_map_component_registers(port);
> > +		if (rc)
> > +			return ERR_PTR(rc);
> > +	}
> > +
> >  	return port;
> >  
> >  err:
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index 0068b5ff5f3e..85fe42abd29b 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -185,7 +185,8 @@ static void cxl_memdev_unregister(void *_cxlmd)
> >  }
> >  
> >  static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
> > -					   const struct file_operations *fops)
> > +					   const struct file_operations *fops,
> > +					   unsigned long component_reg_phys)
> >  {
> >  	struct cxl_memdev *cxlmd;
> >  	struct device *dev;
> > @@ -200,6 +201,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
> >  	if (rc < 0)
> >  		goto err;
> >  	cxlmd->id = rc;
> > +	cxlmd->component_reg_phys = component_reg_phys;
> >  
> >  	dev = &cxlmd->dev;
> >  	device_initialize(dev);
> > @@ -275,15 +277,16 @@ static const struct file_operations cxl_memdev_fops = {
> >  	.llseek = noop_llseek,
> >  };
> >  
> > -struct cxl_memdev *
> > -devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm)
> > +struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> > +				       struct cxl_mem *cxlm,
> > +				       unsigned long component_reg_phys)
> >  {
> >  	struct cxl_memdev *cxlmd;
> >  	struct device *dev;
> >  	struct cdev *cdev;
> >  	int rc;
> >  
> > -	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
> > +	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops, component_reg_phys);
> >  	if (IS_ERR(cxlmd))
> >  		return cxlmd;
> >  
> > diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> > index 8535a7b94f28..4ba75fb6779f 100644
> > --- a/drivers/cxl/core/regs.c
> > +++ b/drivers/cxl/core/regs.c
> > @@ -145,9 +145,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> >  }
> >  EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
> >  
> > -static void __iomem *devm_cxl_iomap_block(struct device *dev,
> > -					  resource_size_t addr,
> > -					  resource_size_t length)
> > +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> > +				   resource_size_t length)
> >  {
> >  	void __iomem *ret_val;
> >  	struct resource *res;
> > @@ -166,6 +165,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,
> >  
> >  	return ret_val;
> >  }
> > +EXPORT_SYMBOL_GPL(devm_cxl_iomap_block);
> >  
> >  int cxl_map_component_regs(struct pci_dev *pdev,
> >  			   struct cxl_component_regs *regs,
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index a168520d741b..4585d03a0a67 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -149,6 +149,8 @@ struct cxl_register_map {
> >  	};
> >  };
> >  
> > +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> > +				   resource_size_t length);
> >  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,
> > @@ -252,6 +254,7 @@ struct cxl_walk_context {
> >   * @dports: cxl_dport instances referenced by decoders
> >   * @decoder_ida: allocator for decoder ids
> >   * @component_reg_phys: component register capability base address (optional)
> > + * @regs: Mapped version of @component_reg_phys
> >   */
> >  struct cxl_port {
> >  	struct device dev;
> > @@ -260,6 +263,7 @@ struct cxl_port {
> >  	struct list_head dports;
> >  	struct ida decoder_ida;
> >  	resource_size_t component_reg_phys;
> > +	struct cxl_component_regs regs;
> >  };
> >  
> >  /**
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 88264204c4b9..f94624e43b2e 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -41,6 +41,7 @@ struct cxl_memdev {
> >  	struct cdev cdev;
> >  	struct cxl_mem *cxlm;
> >  	int id;
> > +	unsigned long component_reg_phys;
> >  };
> >  
> >  static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> > @@ -49,7 +50,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> >  }
> >  
> >  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> > -				       struct cxl_mem *cxlm);
> > +				       struct cxl_mem *cxlm,
> > +				       unsigned long component_reg_phys);
> >  
> >  bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> >  
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index 9d5a3a29cda1..aba9a07d519f 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -73,9 +73,8 @@ static int cxl_mem_probe(struct device *dev)
> >  	if (!port_dev)
> >  		return -ENODEV;
> >  
> > -	/* TODO: Obtain component registers */
> >  	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
> > -					       CXL_RESOURCE_NONE,
> > +					       cxlmd->component_reg_phys,
> >  					       to_cxl_port(port_dev)));
> >  	if (rc)
> >  		dev_err(dev, "Unable to add devices upstream port");
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index e4b3549c4580..258190febb5a 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -382,8 +382,12 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
> >  
> >  	switch (map->reg_type) {
> >  	case CXL_REGLOC_RBI_COMPONENT:
> > +#ifndef CONFIG_CXL_MEM
> >  		cxl_map_component_regs(pdev, &cxlm->regs.component, map);
> >  		dev_dbg(dev, "Mapping component registers...\n");
> > +#else
> > +		dev_dbg(dev, "Component registers not mapped for %s\n", KBUILD_MODNAME);
> > +#endif
> >  		break;
> >  	case CXL_REGLOC_RBI_MEMDEV:
> >  		cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
> > @@ -493,10 +497,11 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps
> >  
> >  static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  {
> > +	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
> >  	struct cxl_register_map maps[CXL_REGLOC_RBI_TYPES];
> >  	struct cxl_memdev *cxlmd;
> >  	struct cxl_mem *cxlm;
> > -	int rc;
> > +	int rc, i;
> >  
> >  	/*
> >  	 * Double check the anonymous union trickery in struct cxl_regs
> > @@ -533,7 +538,17 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> >  	if (rc)
> >  		return rc;
> >  
> > -	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm);
> > +	for (i = 0; i < ARRAY_SIZE(maps); i++) {
> > +		struct cxl_register_map *map = &maps[i];
> > +
> > +		if (map->reg_type != CXL_REGLOC_RBI_COMPONENT)
> > +			continue;
> > +
> > +		component_reg_phys = pci_resource_start(pdev, map->barno) +
> > +				     map->block_offset;
> > +	}
> > +
> > +	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, component_reg_phys);
> >  	if (IS_ERR(cxlmd))
> >  		return PTR_ERR(cxlmd);
> >  
> > -- 
> > 2.33.0
> > 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 01/13] Documentation/cxl: Add bus internal docs
  2021-09-02 19:50 ` [PATCH 01/13] Documentation/cxl: Add bus internal docs Ben Widawsky
@ 2021-09-03 14:05   ` Jonathan Cameron
  2021-09-10 18:20     ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:05 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:05 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Kernel docs are already present in this file, but nothing is instructed
> to generate them. Address that.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

FWIW
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Arguably nothing to do with the later bits of the set though so
I'd have had these doc improvements in a mini set of their own.



> ---
>  Documentation/driver-api/cxl/memory-devices.rst | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> index 356f70d28316..a18175bae7a6 100644
> --- a/Documentation/driver-api/cxl/memory-devices.rst
> +++ b/Documentation/driver-api/cxl/memory-devices.rst
> @@ -39,6 +39,9 @@ CXL Core
>  .. kernel-doc:: drivers/cxl/core/bus.c
>     :doc: cxl core
>  
> +.. kernel-doc:: drivers/cxl/core/bus.c
> +   :identifiers:
> +
>  .. kernel-doc:: drivers/cxl/core/pmem.c
>     :internal:
>  


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops
  2021-09-02 19:50 ` [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops Ben Widawsky
@ 2021-09-03 14:17   ` Jonathan Cameron
  2021-09-10 18:51   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:17 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:06 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Since the code to add decoders for switches and endpoints is on the
> horizon, document the new interfaces that will be consumed by them.

Don't look "new" given they are already there...

> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 3991ac231c3e..9d98dd50d424 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -453,6 +453,19 @@ int cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id,
>  }
>  EXPORT_SYMBOL_GPL(cxl_add_dport);
>  
> +/**
> + * cxl_decoder_alloc - Allocate a new CXL decoder
> + * @port: owning port of this decoder
> + * @nr_targets: downstream targets accessible by this decoder
number of downstream targets accessible by this decoder

perhaps.  Would be an odd name for a bitmap of them for example but the
help text doesn't rule that out.

> + *
> + * A port should contain one or more decoders. Each of those decoders enable
> + * some address space for CXL.mem utilization. Therefore, it is logical to
> + * allocate decoders while enumerating a port. While >= 1 is defined by the CXL
> + * specification, due to error conditions it is possible that a port may have 0
> + * decoders.
> + *
> + * Return: A new cxl decoder which wants to be added with cxl_decoder_add()

CXL decoder

Anything else possible?

> + */
>  struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  {
>  	struct cxl_decoder *cxld;
> @@ -491,6 +504,21 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  }
>  EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
>  
> +/**
> + * cxl_decoder_add - Add a decoder with targets
> + * @host: The containing struct device. This is typically the PCI device that is
> + *        CXL capable

I would drop the 'struct' from that statement and just have device otherwise
it seems to imply something code related rather than physical device related.

> + * @cxld: The cxl decoder allocated by cxl_decoder_alloc()

CXL decoder

> + * @target_map: A list of downstream ports that this decoder can direct memory
> + *              traffic to. These numbers should correspond with the port number
> + *              in the PCIe Link Capabilities structure.
> + *
> + * Return: 0 if decoder was successfully added.

What else is possible?

> + *
> + * Certain types of decoders may not have any targets. The main example of this
> + * is an endpoint device. A more awkward example is a hostbridge whose root
> + * ports get hot added (technically possible, though unlikely).
> + */
>  int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
>  		    int *target_map)
>  {


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 03/13] cxl/core: Ignore interleave when adding decoders
  2021-09-02 19:50 ` [PATCH 03/13] cxl/core: Ignore interleave when adding decoders Ben Widawsky
@ 2021-09-03 14:25   ` Jonathan Cameron
  2021-09-10 19:00     ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:25 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:07 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Decoders will be added to the bus either already active (committed in
> spec parlance), or inactive. From the driver perspective, the set of
> devices comprising the former are those which are brought up by system
> firmware; decoders that implement: volatile regions, persistent regions,
> or platform specific (ie. CFMWS) constraints. Such devices have a given
> interleave programming already in place. Inactive decoders on the other
> hand, do not have any interleave programming in place. The set of
> devices comprising that are hostbridges, switches, and endpoint devices.
> 
> Allow adding inactive decoders by removing this check.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
Makes sense. I assume there is no easy way to modify this check to
still be applied if the encoder is active?

Otherwise

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
 
> ---
>  drivers/cxl/core/bus.c | 3 ---
>  1 file changed, 3 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 9d98dd50d424..8d5061b0794d 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -532,9 +532,6 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
>  	if (IS_ERR(cxld))
>  		return PTR_ERR(cxld);
>  
> -	if (cxld->interleave_ways < 1)
> -		return -EINVAL;
> -
>  	port = to_cxl_port(cxld->dev.parent);
>  	device_lock(&port->dev);
>  	if (list_empty(&port->dports)) {


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-02 19:50 ` [PATCH 04/13] cxl: Introduce endpoint decoders Ben Widawsky
@ 2021-09-03 14:35   ` Jonathan Cameron
  2021-09-13 16:19     ` Ben Widawsky
  2021-09-10 19:19   ` Dan Williams
  1 sibling, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:35 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:08 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Endpoints have decoders too. It is useful to share the same
> infrastructure from cxl_core. Endpoints do not have dports (downstream
> targets), only the underlying physical medium. As a result, some special
> casing is needed.
> 
> There is no functional change introduced yet as endpoints don't actually
> enumerate decoders yet.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

Comments inline...

> ---
>  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
>  1 file changed, 25 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 8d5061b0794d..6202ce5a5ac2 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
>  	NULL,
>  };
>  
> +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> +	&cxl_decoder_base_attribute_group,
> +	&cxl_base_attribute_group,
> +	NULL,
> +};
> +
>  static void cxl_decoder_release(struct device *dev)
>  {
>  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
> @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
>  	kfree(cxld);
>  }
>  
> +static const struct device_type cxl_decoder_endpoint_type = {
> +	.name = "cxl_decoder_endpoint",
> +	.release = cxl_decoder_release,
> +	.groups = cxl_decoder_endpoint_attribute_groups,
> +};
> +
>  static const struct device_type cxl_decoder_switch_type = {
>  	.name = "cxl_decoder_switch",
>  	.release = cxl_decoder_release,
> @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
>  	.groups = cxl_decoder_root_attribute_groups,
>  };
>  
> +static bool is_endpoint_decoder(struct device *dev)
> +{
> +	return dev->type == &cxl_decoder_endpoint_type;
> +}
> +
>  bool is_root_decoder(struct device *dev)
>  {
>  	return dev->type == &cxl_decoder_root_type;
> @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  	struct device *dev;
>  	int rc = 0;
>  
> -	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)

Why do we let nr_targets go negative?  Could make the parameter unsigned perhaps or
check for that here (even though it makes no sense).

> +	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
>  		return ERR_PTR(-EINVAL);
>  
>  	cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  	dev->parent = &port->dev;
>  	dev->bus = &cxl_bus_type;
>  
> +	/* Endpoints don't have a target list */
> +	if (nr_targets == 0)
> +		dev->type = &cxl_decoder_endpoint_type;

Hmm. Whilst the check seems valid, it's a bit inelegant.
The capability register unhelpfully simply has it defined
as not applicable rather than 0, so I'd be nervous that might
end up here (not checked yet).

>  	/* root ports do not have a cxl_port_type parent */
> -	if (port->dev.parent->type == &cxl_port_type)
> +	else if (port->dev.parent->type == &cxl_port_type)
>  		dev->type = &cxl_decoder_switch_type;
>  	else
>  		dev->type = &cxl_decoder_root_type;
> @@ -532,9 +552,11 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
>  	if (IS_ERR(cxld))
>  		return PTR_ERR(cxld);
>  
> +	dev = &cxld->dev;
> +
>  	port = to_cxl_port(cxld->dev.parent);
>  	device_lock(&port->dev);
> -	if (list_empty(&port->dports)) {
> +	if (is_endpoint_decoder(dev) && list_empty(&port->dports)) {
>  		rc = -EINVAL;
>  		goto out_unlock;
>  	}
> @@ -551,7 +573,6 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
>  	}
>  	device_unlock(&port->dev);
>  
> -	dev = &cxld->dev;
>  	rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
>  	if (rc)
>  		return rc;


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem
  2021-09-02 19:50 ` [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem Ben Widawsky
@ 2021-09-03 14:45   ` Jonathan Cameron
  2021-09-10 19:27   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:45 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:09 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Commit 21e9f76733a8 ("cxl: Rename mem to pci") introduced the cxl_pci
> driver which had formerly been named cxl_mem. At the time, the goal was
> to be as light touch as possible because there were other patches in
> flight. Since things have settled now, and a new cxl_mem driver will be
> introduced shortly, spend the LOC now to clean up the existing names.
> 
> While here, fix the kernel docs to explain the situation better after
> the core rework that has already landed.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

One thing below - otherwise fine. With that fixed.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  drivers/cxl/pci.c | 70 +++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 35 deletions(-)
> 

...

> @@ -569,17 +569,17 @@ static const struct pci_device_id cxl_mem_pci_tbl[] = {
>  	{ PCI_DEVICE_CLASS((PCI_CLASS_MEMORY_CXL << 8 | CXL_MEMORY_PROGIF), ~0)},
>  	{ /* terminate list */ },
>  };
> -MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);
> +MODULE_DEVICE_TABLE(pci, cxl_pci_tbl);

Doesn't look like you have renamed the table this is using...


>  
> -static struct pci_driver cxl_mem_driver = {
> +static struct pci_driver cxl_pci_driver = {
>  	.name			= KBUILD_MODNAME,
>  	.id_table		= cxl_mem_pci_tbl,

As above, table rename?

> -	.probe			= cxl_mem_probe,
> +	.probe			= cxl_pci_probe,
>  	.driver	= {
>  		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
>  	},
>  };
>  
>  MODULE_LICENSE("GPL v2");
> -module_pci_driver(cxl_mem_driver);
> +module_pci_driver(cxl_pci_driver);
>  MODULE_IMPORT_NS(CXL);


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 06/13] cxl/mem: Introduce cxl_mem driver
  2021-09-02 19:50 ` [PATCH 06/13] cxl/mem: Introduce cxl_mem driver Ben Widawsky
@ 2021-09-03 14:52   ` Jonathan Cameron
  2021-09-10 21:32   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 14:52 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:10 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> CXL endpoints that participate in the CXL.mem protocol require extra
> control to ensure architectural constraints are met for device
> management. The most straight-forward way to achieve control of these
> endpoints is with a new driver that can bind to such devices. This
> driver will also be responsible for enumerating the switches that
> connect the endpoint to the hostbridge.
> 
> cxl_core already understands the concept of a memdev, but the core [by
> design] does not comprehend all the topological constraints.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

Looks fairly standard... FWIW

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  .../driver-api/cxl/memory-devices.rst         |  3 ++
>  drivers/cxl/Makefile                          |  3 +-
>  drivers/cxl/core/bus.c                        |  2 +
>  drivers/cxl/core/core.h                       |  1 +
>  drivers/cxl/core/memdev.c                     |  2 +-
>  drivers/cxl/cxl.h                             |  1 +
>  drivers/cxl/mem.c                             | 49 +++++++++++++++++++
>  7 files changed, 59 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/cxl/mem.c
> 
> diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> index a18175bae7a6..00d141071570 100644
> --- a/Documentation/driver-api/cxl/memory-devices.rst
> +++ b/Documentation/driver-api/cxl/memory-devices.rst
> @@ -28,6 +28,9 @@ CXL Memory Device
>  .. kernel-doc:: drivers/cxl/pci.c
>     :internal:
>  
> +.. kernel-doc:: drivers/cxl/mem.c
> +   :doc: cxl mem
> +
>  CXL Core
>  --------
>  .. kernel-doc:: drivers/cxl/cxl.h
> diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
> index d1aaabc940f3..d912ac4e3f0c 100644
> --- a/drivers/cxl/Makefile
> +++ b/drivers/cxl/Makefile
> @@ -1,9 +1,10 @@
>  # SPDX-License-Identifier: GPL-2.0
>  obj-$(CONFIG_CXL_BUS) += core/
> -obj-$(CONFIG_CXL_MEM) += cxl_pci.o
> +obj-$(CONFIG_CXL_MEM) += cxl_mem.o cxl_pci.o
>  obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
>  obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
>  
> +cxl_mem-y := mem.o
>  cxl_pci-y := pci.o
>  cxl_acpi-y := acpi.o
>  cxl_pmem-y := pmem.o
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 6202ce5a5ac2..256e55dc2a3b 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -641,6 +641,8 @@ static int cxl_device_id(struct device *dev)
>  		return CXL_DEVICE_NVDIMM_BRIDGE;
>  	if (dev->type == &cxl_nvdimm_type)
>  		return CXL_DEVICE_NVDIMM;
> +	if (dev->type == &cxl_memdev_type)
> +		return CXL_DEVICE_ENDPOINT;
>  	return 0;
>  }
>  
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index e0c9aacc4e9c..dea246cb7c58 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -6,6 +6,7 @@
>  
>  extern const struct device_type cxl_nvdimm_bridge_type;
>  extern const struct device_type cxl_nvdimm_type;
> +extern const struct device_type cxl_memdev_type;
>  
>  extern struct attribute_group cxl_base_attribute_group;
>  
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index ee61202c7aab..c9dd054bd813 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -127,7 +127,7 @@ static const struct attribute_group *cxl_memdev_attribute_groups[] = {
>  	NULL,
>  };
>  
> -static const struct device_type cxl_memdev_type = {
> +const struct device_type cxl_memdev_type = {
>  	.name = "cxl_memdev",
>  	.release = cxl_memdev_release,
>  	.devnode = cxl_memdev_devnode,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 708bfe92b596..b48bdbefd949 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -315,6 +315,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
>  
>  #define CXL_DEVICE_NVDIMM_BRIDGE	1
>  #define CXL_DEVICE_NVDIMM		2
> +#define CXL_DEVICE_ENDPOINT		3
>  
>  #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
>  #define CXL_MODALIAS_FMT "cxl:t%d"
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> new file mode 100644
> index 000000000000..978a54b0a51a
> --- /dev/null
> +++ b/drivers/cxl/mem.c
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> +#include <linux/device.h>
> +#include <linux/module.h>
> +
> +#include "cxlmem.h"
> +
> +/**
> + * DOC: cxl mem
> + *
> + * CXL memory endpoint devices and switches are CXL capable devices that are
> + * participating in CXL.mem protocol. Their functionality builds on top of the
> + * CXL.io protocol that allows enumerating and configuring components via
> + * standard PCI mechanisms.
> + *
> + * The cxl_mem driver implements enumeration and control over these CXL
> + * components.
> + */
> +
> +static int cxl_mem_probe(struct device *dev)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static void cxl_mem_remove(struct device *dev)
> +{
> +}
> +
> +static struct cxl_driver cxl_mem_driver = {
> +	.name = "cxl_mem",
> +	.probe = cxl_mem_probe,
> +	.remove = cxl_mem_remove,
> +	.id = CXL_DEVICE_ENDPOINT,
> +};
> +
> +static __init int cxl_mem_init(void)
> +{
> +	return cxl_driver_register(&cxl_mem_driver);
> +}
> +
> +static __exit void cxl_mem_exit(void)
> +{
> +	cxl_driver_unregister(&cxl_mem_driver);
> +}
> +
> +MODULE_LICENSE("GPL v2");
> +module_init(cxl_mem_init);
> +module_exit(cxl_mem_exit);
> +MODULE_IMPORT_NS(CXL);


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-02 19:50 ` [PATCH 07/13] cxl/memdev: Determine CXL.mem capability Ben Widawsky
@ 2021-09-03 15:21   ` Jonathan Cameron
  2021-09-13 19:01     ` Ben Widawsky
  2021-09-10 21:59   ` Dan Williams
  1 sibling, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 15:21 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny,
	Vishal Verma, Bjorn Helgaas

On Thu, 2 Sep 2021 12:50:11 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> If the "upstream" port of the endpoint is an enumerated downstream CXL
> port, and the device itself is CXL capable and enabled, the memdev
> driver can bind. This binding useful for region configuration/creation
> because it provides a clean way for the region code to determine if the
> memdev is actually CXL capable.
> 
> A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> the end of ACPI probing (see comment in code for details). Switch
> enumeration will be done as a follow-on patch. As a result, if a switch
> is in the topology the memdev driver will not bind to any devices.
> 
> CXL.mem capability is checked lazily at the time a region is bound.
> This is in line with the other configuration parameters.
> 
> Below is an example (mem0, and mem1) of CXL memdev devices that now
> exist on the bus.
> 
> /sys/bus/cxl/devices/
> ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
> ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> └── root0 -> ../../../devices/platform/ACPI0017:00/root0
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

+CC Bjorn.  Given we are moving the (nearly) generic DVSEC lookup routine, perhaps now
is time to move it into PCI core code?

> ---
>  drivers/cxl/acpi.c        | 27 +++++++-----------
>  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/memdev.c |  6 ++++
>  drivers/cxl/cxl.h         |  2 ++
>  drivers/cxl/cxlmem.h      |  2 ++
>  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
>  drivers/cxl/pci.c         | 23 ---------------
>  drivers/cxl/pci.h         |  7 ++++-
>  8 files changed, 141 insertions(+), 41 deletions(-)
> 

...

> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 256e55dc2a3b..56f57302d27b 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c

...

> @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
>  }
>  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
>  
> +/**
> + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> + * @pdev: PCI device to search for the DVSEC
> + * @dvsec: DVSEC id to look for
> + *
> + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> + */
> +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> +{
> +	int pos;
> +
> +	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> +	if (!pos)
> +		return 0;
> +
> +	while (pos) {
> +		u16 vendor, id;
> +
> +		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> +		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> +		if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> +			return pos;
> +
> +		pos = pci_find_next_ext_capability(pdev, pos,
> +						   PCI_EXT_CAP_ID_DVSEC);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);

That's not going to work. (pci/mem)  + Can we move this to the PCI core now?
Wasn't done originally because there were several copies in various different
trees that needed to all come together. Oddly only this one seems to have
made it in though.  


> +
>  /**
>   * __cxl_driver_register - register a driver for the cxl bus
>   * @cxl_drv: cxl driver structure to attach
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index c9dd054bd813..0068b5ff5f3e 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
>  {
>  	unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
>  }
> +
> +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> +{

This feels like it needs a comment to say why that's a valid check to use
to find out if it is mem capable.

> +	return !!cxlmd->dev.driver;
> +}
> +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index b48bdbefd949..a168520d741b 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>  				   resource_size_t component_reg_phys,
>  				   struct cxl_port *parent_port);
>  
> +bool is_cxl_port(struct device *dev);
>  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
>  		  resource_size_t component_reg_phys);
> +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
>  
>  struct cxl_decoder *to_cxl_decoder(struct device *dev);
>  bool is_root_decoder(struct device *dev);
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 811b24451604..88264204c4b9 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
>  				       struct cxl_mem *cxlm);
>  
> +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> +
>  /**
>   * struct cxl_mbox_cmd - A command to be submitted to hardware.
>   * @opcode: (input) The command set and command submitted to hardware.
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 978a54b0a51a..b6dc34d18a86 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -2,8 +2,10 @@
>  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
>  #include <linux/device.h>
>  #include <linux/module.h>
> +#include <linux/pci.h>
>  
>  #include "cxlmem.h"
> +#include "pci.h"
>  
>  /**
>   * DOC: cxl mem
> @@ -17,9 +19,60 @@
>   * components.
>   */
>  
> +static int port_match(struct device *dev, const void *data)
> +{
> +	struct cxl_port *port;
> +
> +	if (!is_cxl_port(dev))
> +		return 0;
> +
> +	port = to_cxl_port(dev);
> +
> +	if (find_dport_by_dev(port, (struct device *)data))
Why the cast?

If this isn't modified in later patches

	return find_dport_by_dev(port, data);


> +		return 1;
> +
> +	return 0;
> +}
> +
> +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> +{
> +	int pcie_dvsec;
> +	u16 dvsec_ctrl;
> +
> +	pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> +	if (!pcie_dvsec) {
> +		dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> +		return false;
> +	}
> +
> +	pci_read_config_word(pdev,
> +			     pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> +			     &dvsec_ctrl);
> +	if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> +		dev_info(&pdev->dev, "CXL.mem protocol not supported on device");

In the ctrl field that indicates it's not on, rather than not supported.
Hence I think the dev_info message is wrong.

> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  static int cxl_mem_probe(struct device *dev)
>  {
> -	return -EOPNOTSUPP;
> +	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> +	struct cxl_mem *cxlm = cxlmd->cxlm;
> +	struct device *pdev_parent = cxlm->dev->parent;
> +	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
> +	struct device *port_dev;
> +
> +	if (!is_cxl_mem_enabled(pdev))
> +		return -ENODEV;
> +
> +	/* TODO: if parent is a switch, this will fail. */
> +	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
> +	if (!port_dev)
> +		return -ENODEV;
> +
> +	return 0;
>  }
>  
>  static void cxl_mem_remove(struct device *dev)

...

> diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
> index 8c1a58813816..d6b9978d05b0 100644
> --- a/drivers/cxl/pci.h
> +++ b/drivers/cxl/pci.h
> @@ -11,7 +11,10 @@
>   */
>  #define PCI_DVSEC_HEADER1_LENGTH_MASK	GENMASK(31, 20)
>  #define PCI_DVSEC_VENDOR_ID_CXL		0x1E98
> -#define PCI_DVSEC_ID_CXL		0x0
> +
> +#define PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID	0x0

Why the rename to this?  DVSEC x3???  Can we get away with something like...

> +#define PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET	0xC
> +#define   CXL_PCIE_MEM_ENABLE			BIT(2)
>  
>  #define PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID	0x8

Mind you I'm not clear why this one has DVSEC twice either...

>  #define PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET	0xC

This one has nothing to do with DVSEC ID... 

> @@ -29,4 +32,6 @@
>  
>  #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
>  
> +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
> +
>  #endif /* __CXL_PCI_H__ */


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 08/13] cxl/mem: Add memdev as a port
  2021-09-02 19:50 ` [PATCH 08/13] cxl/mem: Add memdev as a port Ben Widawsky
@ 2021-09-03 15:31   ` Jonathan Cameron
  2021-09-10 23:09   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 15:31 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:12 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> CXL endpoints contain HDM decoders that are architecturally the same as
> a CXL switch, or a CXL hostbridge. While some restrictions are in place
> for endpoints, they will require the same enumeration logic to determine
> the number and abilities of the HDM decoders.
> 
> Utilizing the existing port APIs from cxl_core is the simplest way to
> gain access to the same set of information that switches and hostbridges
> have.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
In of itself seems sensible.  (note I'm reviewing these one at a time but
reserve the right to throw my hands up in horror at the end result ;)

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  drivers/cxl/core/bus.c |  5 ++++-
>  drivers/cxl/mem.c      | 10 +++++++++-
>  2 files changed, 13 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 56f57302d27b..f26095b40f5c 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -377,7 +377,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>  
>  	dev = &port->dev;
>  	if (parent_port)
> -		rc = dev_set_name(dev, "port%d", port->id);
> +		if (host->type == &cxl_memdev_type)
> +			rc = dev_set_name(dev, "devport%d", port->id);
> +		else
> +			rc = dev_set_name(dev, "port%d", port->id);
>  	else
>  		rc = dev_set_name(dev, "root%d", port->id);
>  	if (rc)
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index b6dc34d18a86..9d5a3a29cda1 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -63,6 +63,7 @@ static int cxl_mem_probe(struct device *dev)
>  	struct device *pdev_parent = cxlm->dev->parent;
>  	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
>  	struct device *port_dev;
> +	int rc;
>  
>  	if (!is_cxl_mem_enabled(pdev))
>  		return -ENODEV;
> @@ -72,7 +73,14 @@ static int cxl_mem_probe(struct device *dev)
>  	if (!port_dev)
>  		return -ENODEV;
>  
> -	return 0;
> +	/* TODO: Obtain component registers */
> +	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,

oh. Nasty / efficient depending on how you look at it.

> +					       CXL_RESOURCE_NONE,
> +					       to_cxl_port(port_dev)));
> +	if (rc)
> +		dev_err(dev, "Unable to add devices upstream port");
> +
> +	return rc;
>  }
>  
>  static void cxl_mem_remove(struct device *dev)


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
  2021-09-02 22:41   ` Ben Widawsky
@ 2021-09-03 16:14   ` Jonathan Cameron
  2021-09-10 23:52     ` Dan Williams
  2021-09-10 23:44   ` Dan Williams
  2 siblings, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 16:14 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:14 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Component registers are implemented for CXL.mem/cache operations. The
> cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> The driver for managing CXL.mem/cache operations will need the component
> registers mapped and the mapping cannot be shared across two devices.
> 
> For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> exception (perhaps others will exist) where it might be desirable to
> have the cxl_pci driver do negotiation. For this case, it probably will
> make sense to create an ephemeral mapping. Further looking, there might
> need to be a cxl_core mechanism to allow arbitrating access to the
> component registers.


> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

As you predicted I don't like this. Needs some thought on how to get
around the mapping games though and it's Friday afternoon so I'm not
going to offer any concrete answers...

Not totally obvious to me where RAS will be handled as well.
I think we definitely need an arbitration mechanism here.

Wouldn't it have been nice if all these capabilities had been nicely
padded so we could map them individually.  Oh well!
Gut feeling is this will only get worse for future versions of the spec
so we should assume there will be lots of stuff shoved in here.


> ---
>  drivers/cxl/core/bus.c    | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/memdev.c | 11 +++++++----
>  drivers/cxl/core/regs.c   |  6 +++---
>  drivers/cxl/cxl.h         |  4 ++++
>  drivers/cxl/cxlmem.h      |  4 +++-
>  drivers/cxl/mem.c         |  3 +--
>  drivers/cxl/pci.c         | 19 +++++++++++++++++--
>  7 files changed, 73 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index f26095b40f5c..01b6fa8373e4 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -310,6 +310,37 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
>  	return devm_add_action_or_reset(host, cxl_unlink_uport, port);
>  }
>  
> +static int cxl_port_map_component_registers(struct cxl_port *port)
> +{
> +	struct cxl_register_map map;
> +	struct cxl_component_reg_map *comp_map = &map.component_map;
> +	void __iomem *crb;
> +
> +	if (port->component_reg_phys == CXL_RESOURCE_NONE)
> +		return 0;
> +
> +	crb = devm_cxl_iomap_block(&port->dev,
> +				   port->component_reg_phys,
> +				   /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);
> +	if (IS_ERR(crb))
> +		return PTR_ERR(crb);
> +
> +	if (!crb) {
> +		dev_err(&port->dev, "No component registers mapped\n");
> +		return -ENXIO;
> +	}
> +
> +	cxl_probe_component_regs(&port->dev, crb, comp_map);
> +	if (!comp_map->hdm_decoder.valid) {
> +		dev_err(&port->dev, "HDM decoder registers invalid\n");
> +		return -ENXIO;
> +	}
> +
> +	port->regs.hdm_decoder = crb + comp_map->hdm_decoder.offset;
> +
> +	return 0;
> +}
> +
>  static struct cxl_port *cxl_port_alloc(struct device *uport,
>  				       resource_size_t component_reg_phys,
>  				       struct cxl_port *parent_port)
> @@ -398,6 +429,13 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>  	if (rc)
>  		return ERR_PTR(rc);
>  
> +	/* Platform "switch" has no parent port or component registers */
> +	if (parent_port) {
> +		rc = cxl_port_map_component_registers(port);
> +		if (rc)
> +			return ERR_PTR(rc);
> +	}
> +
>  	return port;
>  
>  err:
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 0068b5ff5f3e..85fe42abd29b 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -185,7 +185,8 @@ static void cxl_memdev_unregister(void *_cxlmd)
>  }
>  
>  static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
> -					   const struct file_operations *fops)
> +					   const struct file_operations *fops,
> +					   unsigned long component_reg_phys)
>  {
>  	struct cxl_memdev *cxlmd;
>  	struct device *dev;
> @@ -200,6 +201,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
>  	if (rc < 0)
>  		goto err;
>  	cxlmd->id = rc;
> +	cxlmd->component_reg_phys = component_reg_phys;
>  
>  	dev = &cxlmd->dev;
>  	device_initialize(dev);
> @@ -275,15 +277,16 @@ static const struct file_operations cxl_memdev_fops = {
>  	.llseek = noop_llseek,
>  };
>  
> -struct cxl_memdev *
> -devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm)
> +struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> +				       struct cxl_mem *cxlm,
> +				       unsigned long component_reg_phys)
>  {
>  	struct cxl_memdev *cxlmd;
>  	struct device *dev;
>  	struct cdev *cdev;
>  	int rc;
>  
> -	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
> +	cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops, component_reg_phys);
>  	if (IS_ERR(cxlmd))
>  		return cxlmd;
>  
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 8535a7b94f28..4ba75fb6779f 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -145,9 +145,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
>  }
>  EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
>  
> -static void __iomem *devm_cxl_iomap_block(struct device *dev,
> -					  resource_size_t addr,
> -					  resource_size_t length)
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +				   resource_size_t length)
>  {
>  	void __iomem *ret_val;
>  	struct resource *res;
> @@ -166,6 +165,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,
>  
>  	return ret_val;
>  }
> +EXPORT_SYMBOL_GPL(devm_cxl_iomap_block);
>  
>  int cxl_map_component_regs(struct pci_dev *pdev,
>  			   struct cxl_component_regs *regs,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a168520d741b..4585d03a0a67 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -149,6 +149,8 @@ struct cxl_register_map {
>  	};
>  };
>  
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +				   resource_size_t length);
>  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,
> @@ -252,6 +254,7 @@ struct cxl_walk_context {
>   * @dports: cxl_dport instances referenced by decoders
>   * @decoder_ida: allocator for decoder ids
>   * @component_reg_phys: component register capability base address (optional)
> + * @regs: Mapped version of @component_reg_phys
>   */
>  struct cxl_port {
>  	struct device dev;
> @@ -260,6 +263,7 @@ struct cxl_port {
>  	struct list_head dports;
>  	struct ida decoder_ida;
>  	resource_size_t component_reg_phys;
> +	struct cxl_component_regs regs;
>  };
>  
>  /**
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 88264204c4b9..f94624e43b2e 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -41,6 +41,7 @@ struct cxl_memdev {
>  	struct cdev cdev;
>  	struct cxl_mem *cxlm;
>  	int id;
> +	unsigned long component_reg_phys;
>  };
>  
>  static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> @@ -49,7 +50,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  }
>  
>  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> -				       struct cxl_mem *cxlm);
> +				       struct cxl_mem *cxlm,
> +				       unsigned long component_reg_phys);
>  
>  bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
>  
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 9d5a3a29cda1..aba9a07d519f 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -73,9 +73,8 @@ static int cxl_mem_probe(struct device *dev)
>  	if (!port_dev)
>  		return -ENODEV;
>  
> -	/* TODO: Obtain component registers */
>  	rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
> -					       CXL_RESOURCE_NONE,
> +					       cxlmd->component_reg_phys,
>  					       to_cxl_port(port_dev)));
>  	if (rc)
>  		dev_err(dev, "Unable to add devices upstream port");
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index e4b3549c4580..258190febb5a 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -382,8 +382,12 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
>  
>  	switch (map->reg_type) {
>  	case CXL_REGLOC_RBI_COMPONENT:
> +#ifndef CONFIG_CXL_MEM
>  		cxl_map_component_regs(pdev, &cxlm->regs.component, map);
>  		dev_dbg(dev, "Mapping component registers...\n");
> +#else
> +		dev_dbg(dev, "Component registers not mapped for %s\n", KBUILD_MODNAME);
> +#endif

!!!!!

>  		break;
>  	case CXL_REGLOC_RBI_MEMDEV:
>  		cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
> @@ -493,10 +497,11 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps
>  



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 11/13] cxl/core: Convert decoder range to resource
  2021-09-02 19:50 ` [PATCH 11/13] cxl/core: Convert decoder range to resource Ben Widawsky
@ 2021-09-03 16:16   ` Jonathan Cameron
  2021-09-11  0:59   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 16:16 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:15 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Regions will use the resource API in order to help manage allocated
> space. As regions are children of the decoder, it makes sense that the
> parent host the main resource to be suballocated by the region.
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
Seems sensible to me.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

> ---
>  drivers/cxl/acpi.c     | 12 ++++--------
>  drivers/cxl/core/bus.c |  4 ++--
>  drivers/cxl/cxl.h      |  4 ++--
>  3 files changed, 8 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index fd14094bdb3f..26691313d716 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -125,10 +125,9 @@ static void cxl_add_cfmws_decoders(struct device *dev,
>  
>  		cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
>  		cxld->target_type = CXL_DECODER_EXPANDER;
> -		cxld->range = (struct range) {
> -			.start = cfmws->base_hpa,
> -			.end = cfmws->base_hpa + cfmws->window_size - 1,
> -		};
> +		cxld->res = (struct resource)DEFINE_RES_MEM_NAMED(cfmws->base_hpa,
> +								  cfmws->window_size,
> +								  "cfmws");
>  		cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
>  		cxld->interleave_granularity =
>  			CFMWS_INTERLEAVE_GRANULARITY(cfmws);
> @@ -318,10 +317,7 @@ static int add_host_bridge_uport(struct device *match, void *arg)
>  	cxld->interleave_ways = 1;
>  	cxld->interleave_granularity = PAGE_SIZE;
>  	cxld->target_type = CXL_DECODER_EXPANDER;
> -	cxld->range = (struct range) {
> -		.start = 0,
> -		.end = -1,
> -	};
> +	cxld->res = (struct resource)DEFINE_RES_MEM(0, 0);
>  
>  	device_lock(&port->dev);
>  	dport = list_first_entry(&port->dports, typeof(*dport), list);
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 01b6fa8373e4..d056dbd794a4 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -48,7 +48,7 @@ static ssize_t start_show(struct device *dev, struct device_attribute *attr,
>  {
>  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
>  
> -	return sysfs_emit(buf, "%#llx\n", cxld->range.start);
> +	return sysfs_emit(buf, "%#llx\n", cxld->res.start);
>  }
>  static DEVICE_ATTR_RO(start);
>  
> @@ -57,7 +57,7 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
>  {
>  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
>  
> -	return sysfs_emit(buf, "%#llx\n", range_len(&cxld->range));
> +	return sysfs_emit(buf, "%#llx\n", resource_size(&cxld->res));
>  }
>  static DEVICE_ATTR_RO(size);
>  
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 4585d03a0a67..e610fa9dd6c8 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -192,7 +192,7 @@ enum cxl_decoder_type {
>   * struct cxl_decoder - CXL address range decode configuration
>   * @dev: this decoder's device
>   * @id: kernel device name id
> - * @range: address range considered by this decoder
> + * @res: address space resources considered by this decoder
>   * @interleave_ways: number of cxl_dports in this decode
>   * @interleave_granularity: data stride per dport
>   * @target_type: accelerator vs expander (type2 vs type3) selector
> @@ -203,7 +203,7 @@ enum cxl_decoder_type {
>  struct cxl_decoder {
>  	struct device dev;
>  	int id;
> -	struct range range;
> +	struct resource res;
>  	int interleave_ways;
>  	int interleave_granularity;
>  	enum cxl_decoder_type target_type;


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders
  2021-09-02 19:50 ` [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders Ben Widawsky
@ 2021-09-03 17:43   ` Jonathan Cameron
  2021-09-11  1:37     ` Dan Williams
  2021-09-11  1:13   ` Dan Williams
  1 sibling, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 17:43 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:16 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> As of the CXL 2.0 specification, every port will have between 1 and 10
> HDM decoders available in hardware. These exist in the endpoint, switch,
> and top level hostbridges. HDM decoders are required for configuration
> CXL regions, and therefore enumerating them is an important first step.
> 
> As an example, the below has 4 decoders, a top level CFMWS decoder
> (0.0), a single decoder in a single host bridge (1.0), and two devices
> each with 1 decoder (2.0 and 3.0)
> 
> ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> ├── decoder1.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/decoder1.0
> ├── decoder2.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport2/decoder2.0
> ├── decoder3.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport3/decoder3.0
> 
> Additionally, attributes are added for a port:
> 
> /sys/bus/cxl/devices/port1
> ├── active_decoders
> ├── decoder_count
> ├── decoder_enabled
> ├── max_target_count
> ...
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>

Documentation/ABI/testing/sysfs-bus-cxl  needs updating.

Various minor things inline.


> ---
>  drivers/cxl/core/bus.c | 161 ++++++++++++++++++++++++++++++++++++++++-
>  drivers/cxl/cxl.h      |  54 ++++++++++++--
>  2 files changed, 209 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index d056dbd794a4..b75e42965e89 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -43,6 +43,15 @@ struct attribute_group cxl_base_attribute_group = {
>  	.attrs = cxl_base_attributes,
>  };
>  
> +static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
> +			    char *buf)
> +{
> +	struct cxl_decoder *cxld = to_cxl_decoder(dev);
> +
> +	return sysfs_emit(buf, "%d\n", !!cxld->decoder_enabled);
> +}
> +static DEVICE_ATTR_RO(enabled);
> +
>  static ssize_t start_show(struct device *dev, struct device_attribute *attr,
>  			  char *buf)
>  {
> @@ -130,6 +139,7 @@ static ssize_t target_list_show(struct device *dev,
>  static DEVICE_ATTR_RO(target_list);
>  
>  static struct attribute *cxl_decoder_base_attrs[] = {
> +	&dev_attr_enabled.attr,
>  	&dev_attr_start.attr,
>  	&dev_attr_size.attr,
>  	&dev_attr_locked.attr,
> @@ -249,8 +259,48 @@ static void cxl_port_release(struct device *dev)
>  	kfree(port);
>  }
>  
> +static ssize_t active_decoders_show(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	struct cxl_port *port = to_cxl_port(dev);
> +
> +	return sysfs_emit(buf, "%*pbl\n", port->decoder_cap.count,
> +			  port->used_decoders);
> +}
> +static DEVICE_ATTR_RO(active_decoders);
> +
> +static ssize_t decoder_count_show(struct device *dev,
> +				  struct device_attribute *attr, char *buf)
> +{
> +	struct cxl_port *port = to_cxl_port(dev);
> +
> +	return sysfs_emit(buf, "%d\n", port->decoder_cap.count);
> +}
> +static DEVICE_ATTR_RO(decoder_count);
> +
> +static ssize_t max_target_count_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	struct cxl_port *port = to_cxl_port(dev);
> +
> +	return sysfs_emit(buf, "%d\n", port->decoder_cap.target_count);
> +}
> +static DEVICE_ATTR_RO(max_target_count);
> +
> +static struct attribute *cxl_port_caps_attributes[] = {
> +	&dev_attr_active_decoders.attr,
> +	&dev_attr_decoder_count.attr,
> +	&dev_attr_max_target_count.attr,
> +	NULL,
> +};
> +
> +struct attribute_group cxl_port_attribute_group = {
> +	.attrs = cxl_port_caps_attributes,
> +};
> +
>  static const struct attribute_group *cxl_port_attribute_groups[] = {
>  	&cxl_base_attribute_group,
> +	&cxl_port_attribute_group,
>  	NULL,
>  };
>  
> @@ -341,6 +391,107 @@ static int cxl_port_map_component_registers(struct cxl_port *port)
>  	return 0;
>  }
>  
> +static int port_populate_caps(struct cxl_port *port)
> +{
> +	void __iomem *hdm_decoder = port->regs.hdm_decoder;
> +	u32 hdm_cap;
> +
> +	hdm_cap = readl(hdm_decoder + CXL_HDM_DECODER_CAP_OFFSET);
> +
> +	port->used_decoders = devm_bitmap_zalloc(&port->dev,
> +						 cxl_hdm_decoder_count(hdm_cap),
> +						 GFP_KERNEL);

Mentioned below. No advantage that I can see in dynamic allocation.
Also, not really populating anything..

> +	if (!port->used_decoders)
> +		return -ENOMEM;
> +
> +	port->decoder_cap.count = cxl_hdm_decoder_count(hdm_cap);
> +	port->decoder_cap.target_count =
> +		FIELD_GET(CXL_HDM_DECODER_TARGET_COUNT_MASK, hdm_cap);
> +	port->decoder_cap.interleave11_8 =
> +		FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_11_8, hdm_cap);
> +	port->decoder_cap.interleave14_12 =
> +		FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap);
> +
> +	return 0;
> +}
> +
> +static int cxl_port_enumerate_hdm_decoders(struct device *host,
> +					   struct cxl_port *port)
> +{
> +	void __iomem *hdm_decoder = port->regs.hdm_decoder;
> +	u32 hdm_ctrl;
> +	int i, rc = 0;
> +
> +	rc = port_populate_caps(port);
> +	if (rc)
> +		return rc;
> +
> +	if (port->decoder_cap.count == 0) {
> +		dev_warn(host, "Found no HDM decoders\n");

No HDM decoders found.

> +		return -ENODEV;
> +	}
> +
> +	for (i = 0; i < port->decoder_cap.count; i++) {
> +		enum cxl_decoder_type type = CXL_DECODER_EXPANDER;
> +		struct resource res = DEFINE_RES_MEM(0, 0);
> +		struct cxl_decoder *cxld;
> +		int iw = 0, ig = 0;
> +		u32 ctrl;
> +
> +		cxld = cxl_decoder_alloc(port, is_endpoint_decoder(host) ? 0 :
> +					 port->decoder_cap.target_count);
> +		if (IS_ERR(cxld)) {
> +			dev_warn(host, "Failed to allocate the decoder\n");
> +			return PTR_ERR(cxld);
> +		}
> +
> +		ctrl = readl(hdm_decoder + CXL_HDM_DECODER0_CTRL_OFFSET(i));
> +		cxld->decoder_enabled =
> +			!!FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl);
> +		/* If the decoder is already active, parse info */
> +		if (cxld->decoder_enabled) {
> +			set_bit(i, port->used_decoders);
> +			iw = cxl_hdm_decoder_iw(ctrl);
> +			ig = cxl_hdm_decoder_ig(ctrl);
> +			if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0)
> +				type = CXL_DECODER_ACCELERATOR;
> +			res.start = readl(hdm_decoder +
> +					  CXL_HDM_DECODER0_BASE_LOW_OFFSET(i));
> +			res.start |=
> +				(u64)readl(hdm_decoder +
> +					   CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i))
> +				<< 32;

Perhaps worth thinking about exposing if the decoder is locked as well?
Might be good to let userspace know that..

> +		}
> +
> +		cxld->target_type = type;
> +		cxld->res = res;
> +		cxld->interleave_ways = iw;
> +		cxld->interleave_granularity = ig;
> +
> +		rc = cxl_decoder_add(host, cxld, NULL);
> +		if (rc) {
> +			dev_warn(host, "Failed to add decoder (%d)\n", rc);
> +			kfree(cxld);
> +			goto out;
> +		}
> +	}
> +
> +	/*
> +	 * Enable CXL.mem decoding via MMIO for endpoint devices
> +	 *
> +	 * TODO: If a memory device was configured to participate in a region by
> +	 * system firmware via DVSEC, this will break that region.

That seems to me like fatal for this patch set until fixed.
I'm not sure I understand why it will break a region though as I'd assume it
would be already on?

> +	 */
> +	if (is_endpoint_decoder(host)) {
> +		hdm_ctrl = readl(hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
> +		writel(hdm_ctrl | CXL_HDM_DECODER_ENABLE,
> +		       hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
> +	}
> +
> +out:

direct returns are much easier to review...


> +	return rc;
> +}
> +
>  static struct cxl_port *cxl_port_alloc(struct device *uport,
>  				       resource_size_t component_reg_phys,
>  				       struct cxl_port *parent_port)
> @@ -432,8 +583,16 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>  	/* Platform "switch" has no parent port or component registers */
>  	if (parent_port) {
>  		rc = cxl_port_map_component_registers(port);
> -		if (rc)
> +		if (rc) {
> +			dev_err(host, "Failed to map component registers\n");

*grump*  Unrelated change.  I guess you could sort of argue it's needed to
distinguish the different errors, but that's a stretch.


>  			return ERR_PTR(rc);
> +		}
> +
> +		rc = cxl_port_enumerate_hdm_decoders(host, port);
> +		if (rc) {
> +			dev_err(host, "Failed to enumerate HDM decoders\n");
> +			return ERR_PTR(rc);
> +		}
>  	}
>  
>  	return port;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index e610fa9dd6c8..6759fe097e12 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -36,11 +36,19 @@
>  #define CXL_HDM_DECODER_CAP_OFFSET 0x0
>  #define   CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
>  #define   CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
> -#define CXL_HDM_DECODER0_BASE_LOW_OFFSET 0x10
> -#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET 0x14
> -#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET 0x18
> -#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET 0x1c
> -#define CXL_HDM_DECODER0_CTRL_OFFSET 0x20
> +#define   CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
> +#define   CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
> +#define CXL_HDM_DECODER_CTRL_OFFSET 0x0
> +#define   CXL_HDM_DECODER_ENABLE BIT(1)
> +#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x10 + (i) * 0x20)

If you are doing the macro to access higher decoders, don't keep
the DECODER0 naming.  DECODERX perhaps?

> +#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i) (0x14 + (i) * 0x20)
> +#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i) (0x18 + (i) * 0x20)
> +#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i) (0x1c + (i) * 0x20)
> +#define CXL_HDM_DECODER0_CTRL_OFFSET(i) (0x20 + (i) * 0x20)
> +#define   CXL_HDM_DECODER0_CTRL_IG_MASK GENMASK(3, 0)
> +#define   CXL_HDM_DECODER0_CTRL_IW_MASK GENMASK(7, 4)
> +#define   CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
> +#define   CXL_HDM_DECODER0_CTRL_TYPE BIT(12)

Perhaps name this bit to indicate that 1 == type 3 device. Otherwise
need a macro to define type2 and one for type3.

>  
>  static inline int cxl_hdm_decoder_count(u32 cap_hdr)
>  {
> @@ -49,6 +57,20 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr)
>  	return val ? val * 2 : 1;
>  }
>  
> +static inline int cxl_hdm_decoder_ig(u32 ctrl)
> +{
> +	int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl);
> +
> +	return 8 + val;
> +}
> +
> +static inline int cxl_hdm_decoder_iw(u32 ctrl)
> +{
> +	int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl);
> +
> +	return 1 << val;
I guess there isn't a strong reason to clamp this to values the
spec actually allows.  Perhaps a comment to say that for this and ig?
> +}
> +
>  /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
>  #define CXLDEV_CAP_ARRAY_OFFSET 0x0
>  #define   CXLDEV_CAP_ARRAY_CAP_ID 0
> @@ -188,6 +210,12 @@ enum cxl_decoder_type {
>   */
>  #define CXL_DECODER_MAX_INTERLEAVE 16
>  
> +/*
> + * Current specification goes up to 10 double that seems a reasonable
> + * software max for the foreseeable future

I'd stick to 10 until we have evidence otherwise... If you happen to
have a mysterious strong reason to go with 20 though I'm not that fussed
but I suspect others may have equally strong reasons to pick another number ;)

> + */
> +#define CXL_DECODER_MAX_COUNT 20
> +
>  /**
>   * struct cxl_decoder - CXL address range decode configuration
>   * @dev: this decoder's device
> @@ -197,6 +225,7 @@ enum cxl_decoder_type {
>   * @interleave_granularity: data stride per dport

Is it currently documented anywhere that ig is in 2**(ig) bytes?
Might be worth adding if not.

>   * @target_type: accelerator vs expander (type2 vs type3) selector
>   * @flags: memory type capabilities and locking
> + * @decoder_enabled: Is this decoder currently decoding
>   * @nr_targets: number of elements in @target
>   * @target: active ordered target list in current decoder configuration
>   */
> @@ -208,6 +237,7 @@ struct cxl_decoder {
>  	int interleave_granularity;
>  	enum cxl_decoder_type target_type;
>  	unsigned long flags;
> +	bool decoder_enabled;
>  	int nr_targets;
>  	struct cxl_dport *target[];
>  };
> @@ -255,6 +285,12 @@ struct cxl_walk_context {
>   * @decoder_ida: allocator for decoder ids
>   * @component_reg_phys: component register capability base address (optional)
>   * @regs: Mapped version of @component_reg_phys
> + * @used_decoders: Bitmap of currently active decoders for the port
> + * @decoder_cap: Capabilities of all decoders contained by the port
> + * @decoder_cap.count: Count of HDM decoders for the port
> + * @decoder_cap.target_count: Max number of interleaved downstream ports
> + * @decoder_cap.interleave11_8: Are address bits 11-8 available for interleave
> + * @decoder_cap.interleave14_12: Are address bits 14-12 available for interleave
>   */
>  struct cxl_port {
>  	struct device dev;
> @@ -264,6 +300,14 @@ struct cxl_port {
>  	struct ida decoder_ida;
>  	resource_size_t component_reg_phys;
>  	struct cxl_component_regs regs;
> +
> +	unsigned long *used_decoders;

Given it's not huge use appropriate macro to allocate
it directly here with the maximum of 10.
DECLARE_BITMAP() in similar fashion to patch 19 in Dan's
recent series.

https://lore.kernel.org/all/162982122744.1124374.6742215706893563515.stgit@dwillia2-desk3.amr.corp.intel.com/

(I'm liking lore having "all" now !)

> +	struct {
> +		int count;
> +		int target_count;
> +		bool interleave11_8;
> +		bool interleave14_12;
> +	} decoder_cap;
>  };
>  
>  /**


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 13/13] cxl/mem: Enumerate switch decoders
  2021-09-02 19:50 ` [PATCH 13/13] cxl/mem: Enumerate switch decoders Ben Widawsky
@ 2021-09-03 17:56   ` Jonathan Cameron
  2021-09-13 22:12     ` Ben Widawsky
  2021-09-14 23:31   ` Dan Williams
  1 sibling, 1 reply; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-03 17:56 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On Thu, 2 Sep 2021 12:50:17 -0700
Ben Widawsky <ben.widawsky@intel.com> wrote:

> Switches work much in the same way as hostbridges. The primary
> difference is that they are enumerated, and probed via regular PCIe
> mechanisms. A switch has 1 upstream port, and n downstream ports.
> Ultimately a memory device attached to a switch can determine if it's in
> a CXL capable subset of the topology if the switch is CXL capable.
> 
> The algorithm introduced enables enumerating switches in a CXL topology.
> It walks up the topology until it finds a root port (which is enumerated
> by the cxl_acpi driver). Once at the top, it walks back down adding all
> downstream ports along the way.
> 
> Note that practically speaking there can be at most 3 levels of switches
> with the current 2.0 spec. This is because there is a max interleave of
> 8 defined in the spec. If there is a single hostbridge and only 1 root
> port was CXL capable, you could have 3 levels of x2 switches, making
> the x8 interleave. However, as far as the spec is concerned, there can
> be infinite number of switches since a x1 switch is allowed, and
> future versions of the spec may allow for a larger total interleave.

Or you could be lazy and rely on the statement in CXL 2.0 that it supports
only a single level of switching (search for "single level" in 1.4.1)
Lots of other reasons it's far from infinite... (number of busses etc).

I'll not speculate on what might be supported in the future.

A few minor comments below.

Jonathan

> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/mem.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-
>  drivers/cxl/pci.c |   8 ---
>  drivers/cxl/pci.h |   8 +++
>  3 files changed, 137 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index aba9a07d519f..dc8ca43d5bfc 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -56,6 +56,133 @@ static bool is_cxl_mem_enabled(struct pci_dev *pdev)
>  	return true;
>  }
>  
> +/* TODO: dedeuplicate this from drivers/cxl/pci.c? */

That seems like a question with an obvious answer...

> +static unsigned long get_component_regs(struct pci_dev *pdev)
> +{
> +	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
> +	u32 regloc_size, regblocks;
> +	int regloc, i;
> +
> +	regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
> +	if (!regloc) {
> +		dev_err(&pdev->dev, "register location dvsec not found\n");
> +		return component_reg_phys;
> +	}
> +
> +	/* Get the size of the Register Locator DVSEC */
> +	pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
> +	regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
> +
> +	regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
> +	regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
> +
> +	for (i = 0; i < regblocks; i++, regloc += 8) {
> +		u32 reg_lo, reg_hi;
> +		u8 reg_type;
> +		u64 offset;
> +		u8 bar;
> +
> +		pci_read_config_dword(pdev, regloc, &reg_lo);
> +		pci_read_config_dword(pdev, regloc + 4, &reg_hi);
> +
> +		cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,
> +					  &reg_type);
> +
> +		if (reg_type != CXL_REGLOC_RBI_COMPONENT)
> +			continue;
> +
> +		component_reg_phys = pci_resource_start(pdev, bar) + offset;
> +	}
> +
> +	return component_reg_phys;
> +}
> +
> +static void enumerate_uport(struct device *dev)
> +{
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +
> +	/*
> +	 * Parent's parent should be another uport, since we don't have root
> +	 * ports here
> +	 */
> +	if (dev_WARN_ONCE(dev, !dev->parent->parent, "No grandparent port\n"))
> +		return;
> +
> +	if (!is_cxl_port(dev->parent->parent)) {
> +		dev_info(dev, "Parent of uport isn't a CXL port (%s)\n",
> +			 dev_name(dev->parent->parent));
> +		return;
> +	}
> +
> +	devm_cxl_add_port(dev, dev, get_component_regs(pdev),
> +			  to_cxl_port(dev->parent));
> +}
> +
> +static void enumerate_dport(struct device *dev)
> +{
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	u32 port_num, lnkcap;
> +
> +	if (dev_WARN_ONCE(dev, !dev->parent, "No parent port\n"))
> +		return;
> +
> +	if (!is_cxl_port(dev->parent)) {
> +		dev_info(dev, "Uport isn't a CXL port %s\n",
> +			 dev_name(dev->parent));
> +		return;
> +	}
> +
> +	/* TODO: deduplicate from drivers/cxl/acpi.c? */
> +	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
> +				  &lnkcap) != PCIBIOS_SUCCESSFUL)
> +		return;
> +	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
> +
> +	cxl_add_dport(to_cxl_port(dev->parent), dev, port_num,
> +		      get_component_regs(pdev));
> +}
> +
> +/*
> + * Walk up the topology until we get to the root port (ie. parent is a
> + * cxl port). From there walk back down adding the additional ports. If the
> + * parent isn't a PCIe switch (upstream or downstream port), the downstream
> + * endpoint(s) cannot be CXL enabled.
> + *
> + * XXX: It's possible that cxl_acpi hasn't yet enumerated the root ports, and
> + * so that will rescan the CXL bus, thus coming back here.
> + */
> +static void enumerate_switches(struct device *dev)
> +{
> +	struct pci_dev *pdev;
> +	int type;
> +
> +	if (unlikely(!dev))

Unlikely markings seems unlikely to be necessary. I'm assuming
this is far from a hot path!

> +		return;
> +
> +	if (unlikely(!dev_is_pci(dev)))
> +		return;
> +
> +	pdev = to_pci_dev(dev);
> +
> +	if (unlikely(!pci_is_pcie(pdev)))
> +		return;
> +
> +	if (!is_cxl_mem_enabled(pdev))
> +		return;
> +
> +	type = pci_pcie_type(pdev);
> +
> +	if (type != PCI_EXP_TYPE_UPSTREAM && type != PCI_EXP_TYPE_DOWNSTREAM)
> +		return;
> +
> +	enumerate_switches(dev->parent);
> +
> +	if (type == PCI_EXP_TYPE_UPSTREAM)
> +		enumerate_uport(dev);
> +	if (type == PCI_EXP_TYPE_DOWNSTREAM)
> +		enumerate_dport(dev);
> +}
> +
>  static int cxl_mem_probe(struct device *dev)
>  {
>  	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> @@ -68,7 +195,8 @@ static int cxl_mem_probe(struct device *dev)
>  	if (!is_cxl_mem_enabled(pdev))
>  		return -ENODEV;
>  
> -	/* TODO: if parent is a switch, this will fail. */
> +	enumerate_switches(dev->parent);
> +
>  	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
>  	if (!port_dev)
>  		return -ENODEV;


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 00/13] Enumerate midlevel and endpoint decoders
  2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
                   ` (12 preceding siblings ...)
  2021-09-02 19:50 ` [PATCH 13/13] cxl/mem: Enumerate switch decoders Ben Widawsky
@ 2021-09-10 18:15 ` Dan Williams
  13 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 18:15 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Every CXL component may implement component registers as defined by the CXL 2.0
> specification. In preparation for creating and enumerating regions it's
> important to at least enumerate all HDM decoders which are a subset of the
> component registers. To do this, a new cxl_mem driver is introduced which is
> responsible for binding to a CXL.mem enabled device. In order to determine
> whether or not an endpoint is CXL enabled, the relevant subhierarchy must be
> enumerated.
>
> This serves as the stepping stone toward enabling regions because regions must
> be able to determine if the devices selected for the region are CXL.mem capable
> and enabled.
>
> There's two issues that need to be resolved but I'm going to propose we fix
> them next time we need to touch this code...
> 1. cxl_pci now relinquishes its component register mappings. This may be
>    undesirable as cxl_pci may need to use those mappings.
> 2a. some amount of component register enumeration is duplicated in cxl_pci and
>     cxl_mem
> 2b. awkwardness in cxl_mem where memdevs get their component registers from
>    cxl_pci, and ports that enumerate their own component registers

I don't immediately see this as a technical debt problem. The CXL
component registers belong to the corresponding CXL functionality
driver. One agent should be in charge of all of them and any other
agent that has a request must work through the owner. The sub-device
design pattern like registering cxl_memdev on the cxl-bus, or the
generic mechanism to register auxliary-devices on the auxiliary-bus is
specfically targeting this case of parent-driver registering resources
for a child-driver to operate.

>
> The obvious fix for both of these is to move component register mapping to
> cxl_core, and let cxl_core arbitrate the mappings for the "client" drivers.
> Since the code needed to enable cxl_mem was small and subset of the existing
> code (and fairly error resistent vs creating a cxl_core API) I'm hoping to kick
> the can down the road.

The core contains common enumeration and mapping code, but ownership
belongs to one and only one agent, no runtime arbitration. ...or so
I'd like to assert unless there is a exceedingly good reason to add
arbitration complexity.

>
> NOTE: I do not have a way at present to test switches. For this reason and for
> patch readability, the switch enumeration is left as a separate patch.
>
> NOTE2: Some of these patches were introduced in an RFC for region creation. Upon
> further inspection, it made a lot of sense to land these before region creation
> so long as it's understood upfront why the new driver is needed.
>
> I've pushed this to my gitlab here:
> https://gitlab.com/bwidawsk/linux/-/tree/decoders
>
> Ben Widawsky (13):
>   Documentation/cxl: Add bus internal docs
>   cxl/core/bus: Add kernel docs for decoder ops
>   cxl/core: Ignore interleave when adding decoders
>   cxl: Introduce endpoint decoders
>   cxl/pci: Disambiguate cxl_pci further from cxl_mem
>   cxl/mem: Introduce cxl_mem driver
>   cxl/memdev: Determine CXL.mem capability
>   cxl/mem: Add memdev as a port
>   cxl/pci: Retain map information in cxl_mem_probe
>   cxl/core: Map component registers for ports
>   cxl/core: Convert decoder range to resource
>   cxl/core/bus: Enumerate all HDM decoders
>   cxl/mem: Enumerate switch decoders
>
>  .../driver-api/cxl/memory-devices.rst         |   6 +
>  drivers/cxl/Makefile                          |   3 +-
>  drivers/cxl/acpi.c                            |  39 +--
>  drivers/cxl/core/bus.c                        | 326 +++++++++++++++++-
>  drivers/cxl/core/core.h                       |   1 +
>  drivers/cxl/core/memdev.c                     |  19 +-
>  drivers/cxl/core/regs.c                       |   6 +-
>  drivers/cxl/cxl.h                             |  65 +++-
>  drivers/cxl/cxlmem.h                          |   6 +-
>  drivers/cxl/mem.c                             | 237 +++++++++++++
>  drivers/cxl/pci.c                             | 124 +++----
>  drivers/cxl/pci.h                             |  15 +-
>  12 files changed, 727 insertions(+), 120 deletions(-)
>  create mode 100644 drivers/cxl/mem.c
>
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 01/13] Documentation/cxl: Add bus internal docs
  2021-09-03 14:05   ` Jonathan Cameron
@ 2021-09-10 18:20     ` Dan Williams
  0 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 18:20 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Ben Widawsky, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On Fri, Sep 3, 2021 at 7:06 AM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Thu, 2 Sep 2021 12:50:05 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> > Kernel docs are already present in this file, but nothing is instructed
> > to generate them. Address that.
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>
> FWIW
> Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
> Arguably nothing to do with the later bits of the set though so
> I'd have had these doc improvements in a mini set of their own.

As long as whoever merges this knows that it could go into its own
topic branch if need be I think it's easier for contributors to send
straight line sets. My only ask is that the set is organized as fixes
=> cleanups => features. If a mildly "unrelated" change sneaks in not
related with the "feature" patches, that's ok to me.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops
  2021-09-02 19:50 ` [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops Ben Widawsky
  2021-09-03 14:17   ` Jonathan Cameron
@ 2021-09-10 18:51   ` Dan Williams
  2021-09-11 17:25     ` Ben Widawsky
  1 sibling, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 18:51 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Since the code to add decoders for switches and endpoints is on the
> horizon, document the new interfaces that will be consumed by them.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 3991ac231c3e..9d98dd50d424 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -453,6 +453,19 @@ int cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id,
>  }
>  EXPORT_SYMBOL_GPL(cxl_add_dport);
>
> +/**
> + * cxl_decoder_alloc - Allocate a new CXL decoder
> + * @port: owning port of this decoder
> + * @nr_targets: downstream targets accessible by this decoder
> + *
> + * A port should contain one or more decoders. Each of those decoders enable
> + * some address space for CXL.mem utilization. Therefore, it is logical to

I think a "therefore it is logical" statement is changelog fodder.
Once the code is in the kernel it does not need to keep justifying its
existence.

> + * allocate decoders while enumerating a port. While >= 1 is defined by the CXL
> + * specification, due to error conditions it is possible that a port may have 0
> + * decoders.

This comment feels out of place. Why does cxl_decoder_alloc() care how
many decoders a port has? I would expect this comment on a cxl_port
api that is trying to walk decoders.

> + *
> + * Return: A new cxl decoder which wants to be added with cxl_decoder_add()

s/which wants to be added/to be registered by/

> + */
>  struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  {
>         struct cxl_decoder *cxld;
> @@ -491,6 +504,21 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>  }
>  EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
>
> +/**
> + * cxl_decoder_add - Add a decoder with targets
> + * @host: The containing struct device. This is typically the PCI device that is
> + *        CXL capable

No, this is the device doing the enumeration. After the devm removal
for decoder creation it's now only being used to print a debug
message. Do you have another use for it? Perhaps it should just be
deleted. The new cxl_decoder_autoremove() handles what @host was used
for previously.

> + * @cxld: The cxl decoder allocated by cxl_decoder_alloc()
> + * @target_map: A list of downstream ports that this decoder can direct memory
> + *              traffic to. These numbers should correspond with the port number
> + *              in the PCIe Link Capabilities structure.
> + *
> + * Return: 0 if decoder was successfully added.
> + *
> + * Certain types of decoders may not have any targets. The main example of this
> + * is an endpoint device. A more awkward example is a hostbridge whose root
> + * ports get hot added (technically possible, though unlikely).
> + */
>  int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
>                     int *target_map)
>  {
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 03/13] cxl/core: Ignore interleave when adding decoders
  2021-09-03 14:25   ` Jonathan Cameron
@ 2021-09-10 19:00     ` Dan Williams
  2021-09-11 17:30       ` Ben Widawsky
  0 siblings, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 19:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Ben Widawsky, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On Fri, Sep 3, 2021 at 7:25 AM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Thu, 2 Sep 2021 12:50:07 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> > Decoders will be added to the bus either already active (committed in
> > spec parlance), or inactive. From the driver perspective, the set of
> > devices comprising the former are those which are brought up by system
> > firmware; decoders that implement: volatile regions, persistent regions,
> > or platform specific (ie. CFMWS) constraints. Such devices have a given
> > interleave programming already in place. Inactive decoders on the other
> > hand, do not have any interleave programming in place. The set of
> > devices comprising that are hostbridges, switches, and endpoint devices.
> >
> > Allow adding inactive decoders by removing this check.

I thought I agreed with this initially, but the spec initializes the
default value of IW to 0 (== x1 interleave). It is impossible for a
decoder to ever have less than one interleave-way defined. Instead
"Decoder Size == 0" is a disabled decoder.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-02 19:50 ` [PATCH 04/13] cxl: Introduce endpoint decoders Ben Widawsky
  2021-09-03 14:35   ` Jonathan Cameron
@ 2021-09-10 19:19   ` Dan Williams
  2021-09-13 16:11     ` Ben Widawsky
  1 sibling, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 19:19 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Endpoints have decoders too. It is useful to share the same
> infrastructure from cxl_core. Endpoints do not have dports (downstream
> targets), only the underlying physical medium. As a result, some special
> casing is needed.
>
> There is no functional change introduced yet as endpoints don't actually
> enumerate decoders yet.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
>  1 file changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 8d5061b0794d..6202ce5a5ac2 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
>         NULL,
>  };
>
> +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> +       &cxl_decoder_base_attribute_group,
> +       &cxl_base_attribute_group,
> +       NULL,
> +};
> +
>  static void cxl_decoder_release(struct device *dev)
>  {
>         struct cxl_decoder *cxld = to_cxl_decoder(dev);
> @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
>         kfree(cxld);
>  }
>
> +static const struct device_type cxl_decoder_endpoint_type = {
> +       .name = "cxl_decoder_endpoint",
> +       .release = cxl_decoder_release,
> +       .groups = cxl_decoder_endpoint_attribute_groups,
> +};
> +
>  static const struct device_type cxl_decoder_switch_type = {
>         .name = "cxl_decoder_switch",
>         .release = cxl_decoder_release,
> @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
>         .groups = cxl_decoder_root_attribute_groups,
>  };
>
> +static bool is_endpoint_decoder(struct device *dev)
> +{
> +       return dev->type == &cxl_decoder_endpoint_type;
> +}
> +
>  bool is_root_decoder(struct device *dev)
>  {
>         return dev->type == &cxl_decoder_root_type;
> @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>         struct device *dev;
>         int rc = 0;
>
> -       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> +       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
>                 return ERR_PTR(-EINVAL);
>
>         cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
>         dev->parent = &port->dev;
>         dev->bus = &cxl_bus_type;
>
> +       /* Endpoints don't have a target list */
> +       if (nr_targets == 0)
> +               dev->type = &cxl_decoder_endpoint_type;

Do you also plan to introduce the concept of endpoint ports, and if
yes should that come before this patch? That would seem to be more
robust than, for example, allowing a switch port to carry an endpoint
decoder object as this allows.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem
  2021-09-02 19:50 ` [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem Ben Widawsky
  2021-09-03 14:45   ` Jonathan Cameron
@ 2021-09-10 19:27   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 19:27 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Commit 21e9f76733a8 ("cxl: Rename mem to pci") introduced the cxl_pci
> driver which had formerly been named cxl_mem. At the time, the goal was
> to be as light touch as possible because there were other patches in
> flight. Since things have settled now, and a new cxl_mem driver will be
> introduced shortly, spend the LOC now to clean up the existing names.
>
> While here, fix the kernel docs to explain the situation better after
> the core rework that has already landed.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/pci.c | 70 +++++++++++++++++++++++------------------------
>  1 file changed, 35 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index b13884275d96..6931885c83ce 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -16,14 +16,14 @@
>   *
>   * This implements the PCI exclusive functionality for a CXL device as it is
>   * defined by the Compute Express Link specification. CXL devices may surface
> - * certain functionality even if it isn't CXL enabled.
> + * certain functionality even if it isn't CXL enabled. While this driver is
> + * focused around the PCI specific aspects of a CXL device, it binds to the
> + * specific CXL memory device class code, and therefore the implementation of
> + * cxl_pci is focused around CXL memory devices.
>   *
> - * The driver has several responsibilities, mainly:
> + * The driver has two responsibilities:

Perhaps:

s/driver has two responsibilities:/driver's responsibilities include:/

>   *  - Create the memX device and register on the CXL bus.
>   *  - Enumerate device's register interface and map them.

..since there are other things to add here:

It also registers an nvdimm bridge device, and you might mention it
registers the mbox_send mechanism to mailbox core. Later it will also
be responsible for coordinating the retrieval of the CDAT.

> - *  - Probe the device attributes to establish sysfs interface.
> - *  - Provide an IOCTL interface to userspace to communicate with the device for
> - *    things like firmware update.
>   */
>
>  #define cxl_doorbell_busy(cxlm)                                                \
> @@ -33,7 +33,7 @@
>  /* CXL 2.0 - 8.2.8.4 */
>  #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
>
> -static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
> +static int cxl_pci_wait_for_doorbell(struct cxl_mem *cxlm)

Should this be cxl_pci_mbox_wait_for_doobell() to match the other helpers?

Other than that, rename looks good to me.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 06/13] cxl/mem: Introduce cxl_mem driver
  2021-09-02 19:50 ` [PATCH 06/13] cxl/mem: Introduce cxl_mem driver Ben Widawsky
  2021-09-03 14:52   ` Jonathan Cameron
@ 2021-09-10 21:32   ` Dan Williams
  2021-09-13 16:46     ` Ben Widawsky
  1 sibling, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 21:32 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> CXL endpoints that participate in the CXL.mem protocol require extra
> control to ensure architectural constraints are met for device
> management. The most straight-forward way to achieve control of these
> endpoints is with a new driver that can bind to such devices. This
> driver will also be responsible for enumerating the switches that
> connect the endpoint to the hostbridge.
>
> cxl_core already understands the concept of a memdev, but the core [by
> design] does not comprehend all the topological constraints.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  .../driver-api/cxl/memory-devices.rst         |  3 ++
>  drivers/cxl/Makefile                          |  3 +-
>  drivers/cxl/core/bus.c                        |  2 +
>  drivers/cxl/core/core.h                       |  1 +
>  drivers/cxl/core/memdev.c                     |  2 +-
>  drivers/cxl/cxl.h                             |  1 +
>  drivers/cxl/mem.c                             | 49 +++++++++++++++++++
>  7 files changed, 59 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/cxl/mem.c
>
> diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> index a18175bae7a6..00d141071570 100644
> --- a/Documentation/driver-api/cxl/memory-devices.rst
> +++ b/Documentation/driver-api/cxl/memory-devices.rst
> @@ -28,6 +28,9 @@ CXL Memory Device
>  .. kernel-doc:: drivers/cxl/pci.c
>     :internal:
>
> +.. kernel-doc:: drivers/cxl/mem.c
> +   :doc: cxl mem
> +
>  CXL Core
>  --------
>  .. kernel-doc:: drivers/cxl/cxl.h
> diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
> index d1aaabc940f3..d912ac4e3f0c 100644
> --- a/drivers/cxl/Makefile
> +++ b/drivers/cxl/Makefile
> @@ -1,9 +1,10 @@
>  # SPDX-License-Identifier: GPL-2.0
>  obj-$(CONFIG_CXL_BUS) += core/
> -obj-$(CONFIG_CXL_MEM) += cxl_pci.o
> +obj-$(CONFIG_CXL_MEM) += cxl_mem.o cxl_pci.o

I'd rather now have a separate CONFIG_CXL_PCI Kconfig symbol for
cxl_pci and let CONFIG_CXL_MEM just mean cxl_mem. I.e. maybe there
will be a non-cxl_pci agent that registers cxl_memdev objects, or
maybe someone will only want manageability and not CXL.mem operation
enabled in their kernel.


>  obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
>  obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
>
> +cxl_mem-y := mem.o
>  cxl_pci-y := pci.o
>  cxl_acpi-y := acpi.o
>  cxl_pmem-y := pmem.o
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 6202ce5a5ac2..256e55dc2a3b 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -641,6 +641,8 @@ static int cxl_device_id(struct device *dev)
>                 return CXL_DEVICE_NVDIMM_BRIDGE;
>         if (dev->type == &cxl_nvdimm_type)
>                 return CXL_DEVICE_NVDIMM;
> +       if (dev->type == &cxl_memdev_type)
> +               return CXL_DEVICE_ENDPOINT;

Perhaps CXL_DEVICE_MEMORY_{INTERFACE,EXPANDER}? "ENDPOINT" seems too generic.

>         return 0;
>  }
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index e0c9aacc4e9c..dea246cb7c58 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -6,6 +6,7 @@
>
>  extern const struct device_type cxl_nvdimm_bridge_type;
>  extern const struct device_type cxl_nvdimm_type;
> +extern const struct device_type cxl_memdev_type;
>
>  extern struct attribute_group cxl_base_attribute_group;
>
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index ee61202c7aab..c9dd054bd813 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -127,7 +127,7 @@ static const struct attribute_group *cxl_memdev_attribute_groups[] = {
>         NULL,
>  };
>
> -static const struct device_type cxl_memdev_type = {
> +const struct device_type cxl_memdev_type = {
>         .name = "cxl_memdev",
>         .release = cxl_memdev_release,
>         .devnode = cxl_memdev_devnode,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 708bfe92b596..b48bdbefd949 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -315,6 +315,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
>
>  #define CXL_DEVICE_NVDIMM_BRIDGE       1
>  #define CXL_DEVICE_NVDIMM              2
> +#define CXL_DEVICE_ENDPOINT            3
>
>  #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
>  #define CXL_MODALIAS_FMT "cxl:t%d"
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> new file mode 100644
> index 000000000000..978a54b0a51a
> --- /dev/null
> +++ b/drivers/cxl/mem.c
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> +#include <linux/device.h>
> +#include <linux/module.h>
> +
> +#include "cxlmem.h"
> +
> +/**
> + * DOC: cxl mem
> + *
> + * CXL memory endpoint devices and switches are CXL capable devices that are
> + * participating in CXL.mem protocol. Their functionality builds on top of the
> + * CXL.io protocol that allows enumerating and configuring components via
> + * standard PCI mechanisms.
> + *
> + * The cxl_mem driver implements enumeration and control over these CXL
> + * components.
> + */
> +
> +static int cxl_mem_probe(struct device *dev)
> +{
> +       return -EOPNOTSUPP;

Why not just merge this patch with the one that fills these in? I'm
otherwise not understanding the value of having this stopping point in
someone's future bisect run. Even commit 4cdadfd5e0a7 ("cxl/mem:
Introduce a driver for CXL-2.0-Type-3 endpoints") had a functional
probe.

> +}
> +
> +static void cxl_mem_remove(struct device *dev)
> +{
> +}
> +
> +static struct cxl_driver cxl_mem_driver = {
> +       .name = "cxl_mem",
> +       .probe = cxl_mem_probe,
> +       .remove = cxl_mem_remove,

Empty ->remove() callbacks don't need to be registered.

> +       .id = CXL_DEVICE_ENDPOINT,
> +};
> +
> +static __init int cxl_mem_init(void)
> +{
> +       return cxl_driver_register(&cxl_mem_driver);
> +}
> +
> +static __exit void cxl_mem_exit(void)
> +{
> +       cxl_driver_unregister(&cxl_mem_driver);
> +}
> +
> +MODULE_LICENSE("GPL v2");
> +module_init(cxl_mem_init);
> +module_exit(cxl_mem_exit);

Perhaps, before this adds more boilerplate, go define a:

#define module_cxl_driver(__cxl_driver) \
        module_driver(__cxl_driver, cxl_driver_register, cxl_driver_unregister)

...as a lead-in patch?

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-02 19:50 ` [PATCH 07/13] cxl/memdev: Determine CXL.mem capability Ben Widawsky
  2021-09-03 15:21   ` Jonathan Cameron
@ 2021-09-10 21:59   ` Dan Williams
  2021-09-13 22:10     ` Ben Widawsky
  1 sibling, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 21:59 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> If the "upstream" port of the endpoint is an enumerated downstream CXL
> port, and the device itself is CXL capable and enabled, the memdev
> driver can bind. This binding useful for region configuration/creation
> because it provides a clean way for the region code to determine if the
> memdev is actually CXL capable.
>
> A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> the end of ACPI probing (see comment in code for details). Switch
> enumeration will be done as a follow-on patch. As a result, if a switch
> is in the topology the memdev driver will not bind to any devices.
>
> CXL.mem capability is checked lazily at the time a region is bound.
> This is in line with the other configuration parameters.
>
> Below is an example (mem0, and mem1) of CXL memdev devices that now
> exist on the bus.
>
> /sys/bus/cxl/devices/
> ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1

I'm confused, this isn't showing anything new that did not already
exist before this patch? What I think would be a useful shortcut is
for memX devices to have an attribute that links back to their cxl
root port after validation completes. Like an attribute group that
arrives and disappears when the driver successfully binds and unbinds
respectively.

> ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> └── root0 -> ../../../devices/platform/ACPI0017:00/root0
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/acpi.c        | 27 +++++++-----------
>  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/memdev.c |  6 ++++
>  drivers/cxl/cxl.h         |  2 ++
>  drivers/cxl/cxlmem.h      |  2 ++
>  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
>  drivers/cxl/pci.c         | 23 ---------------
>  drivers/cxl/pci.h         |  7 ++++-
>  8 files changed, 141 insertions(+), 41 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 7130beffc929..fd14094bdb3f 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -240,21 +240,6 @@ __mock 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;
> -}
> -
>  __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
>                                               struct device *dev)
>  {
> @@ -459,9 +444,19 @@ static int cxl_acpi_probe(struct platform_device *pdev)
>         if (rc)
>                 goto out;
>
> -       if (IS_ENABLED(CONFIG_CXL_PMEM))
> +       if (IS_ENABLED(CONFIG_CXL_PMEM)) {
>                 rc = device_for_each_child(&root_port->dev, root_port,
>                                            add_root_nvdimm_bridge);
> +               if (rc)
> +                       goto out;
> +       }
> +
> +       /*
> +        * While ACPI is scanning hostbridge ports, switches and memory devices
> +        * may have been probed. Those devices will need to know whether the
> +        * hostbridge is CXL capable.
> +        */
> +       rc = bus_rescan_devices(&cxl_bus_type);

I don't think it's a good idea to call bus_rescan_devices() from
probe() context. This now sets up a lockdep dependency between the
ACPI0017 device-lock and all the device-locks for every device on the
cxl-bus. This is why the nvdimm code punts the rescan outside the lock
to a workqueue.

Lockdep unfortunately won't complain about device-lock entanglements.
One item for the backlog is to add device-lock validation to the cxl
subsystem ala commit 87a30e1f05d7 ("driver-core, libnvdimm: Let device
subsystems add local lockdep coverage")


>
>  out:
>         acpi_put_table(acpi_cedt);
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 256e55dc2a3b..56f57302d27b 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -8,6 +8,7 @@
>  #include <linux/idr.h>
>  #include <cxlmem.h>
>  #include <cxl.h>
> +#include <pci.h>
>  #include "core.h"
>
>  /**
> @@ -259,6 +260,12 @@ static const struct device_type cxl_port_type = {
>         .groups = cxl_port_attribute_groups,
>  };
>
> +bool is_cxl_port(struct device *dev)
> +{
> +       return dev->type == &cxl_port_type;
> +}
> +EXPORT_SYMBOL_GPL(is_cxl_port);
> +
>  struct cxl_port *to_cxl_port(struct device *dev)
>  {
>         if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
> @@ -266,6 +273,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
>                 return NULL;
>         return container_of(dev, struct cxl_port, dev);
>  }
> +EXPORT_SYMBOL_GPL(to_cxl_port);
>
>  static void unregister_port(void *_port)
>  {
> @@ -424,6 +432,27 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
>         return dup ? -EEXIST : 0;
>  }
>
> +/**
> + * find_dport_by_dev - gets downstream CXL port from a struct device
> + * @port: cxl [upstream] port that "owns" the downstream port is being queried
> + * @dev: The device that is backing the downstream port
> + */
> +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const 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;
> +}
> +EXPORT_SYMBOL_GPL(find_dport_by_dev);

This wants to move into the "cxl_" prefix symbol namespace if it's now
going to be a public function.

> +
>  /**
>   * cxl_add_dport - append downstream port data to a cxl_port
>   * @port: the cxl_port that references this dport
> @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
>  }
>  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
>
> +/**
> + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> + * @pdev: PCI device to search for the DVSEC
> + * @dvsec: DVSEC id to look for
> + *
> + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> + */
> +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> +{
> +       int pos;
> +
> +       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> +       if (!pos)
> +               return 0;
> +
> +       while (pos) {
> +               u16 vendor, id;
> +
> +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> +               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> +                       return pos;
> +
> +               pos = pci_find_next_ext_capability(pdev, pos,
> +                                                  PCI_EXT_CAP_ID_DVSEC);
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);

Why not keep this enumeration in cxl_pci and have it record the
component register block base address at cxl_memdev creation time?
This would make it similar to cxl_port creation that takes a
component_register base address argument.

> +
>  /**
>   * __cxl_driver_register - register a driver for the cxl bus
>   * @cxl_drv: cxl driver structure to attach
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index c9dd054bd813..0068b5ff5f3e 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
>  {
>         unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
>  }
> +
> +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> +{
> +       return !!cxlmd->dev.driver;
> +}
> +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);

Perhaps:

s/capable/{enabled,routed}/

The device is always capable, it's the hierarchy that will let it down.

> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index b48bdbefd949..a168520d741b 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>                                    resource_size_t component_reg_phys,
>                                    struct cxl_port *parent_port);
>
> +bool is_cxl_port(struct device *dev);
>  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
>                   resource_size_t component_reg_phys);
> +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
>
>  struct cxl_decoder *to_cxl_decoder(struct device *dev);
>  bool is_root_decoder(struct device *dev);
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 811b24451604..88264204c4b9 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
>                                        struct cxl_mem *cxlm);
>
> +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> +
>  /**
>   * struct cxl_mbox_cmd - A command to be submitted to hardware.
>   * @opcode: (input) The command set and command submitted to hardware.
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 978a54b0a51a..b6dc34d18a86 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -2,8 +2,10 @@
>  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
>  #include <linux/device.h>
>  #include <linux/module.h>
> +#include <linux/pci.h>
>
>  #include "cxlmem.h"
> +#include "pci.h"
>
>  /**
>   * DOC: cxl mem
> @@ -17,9 +19,60 @@
>   * components.
>   */
>
> +static int port_match(struct device *dev, const void *data)
> +{
> +       struct cxl_port *port;
> +
> +       if (!is_cxl_port(dev))
> +               return 0;
> +
> +       port = to_cxl_port(dev);
> +
> +       if (find_dport_by_dev(port, (struct device *)data))
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> +{
> +       int pcie_dvsec;
> +       u16 dvsec_ctrl;
> +
> +       pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> +       if (!pcie_dvsec) {
> +               dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> +               return false;
> +       }
> +
> +       pci_read_config_word(pdev,
> +                            pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> +                            &dvsec_ctrl);
> +       if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> +               dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
> +               return false;
> +       }
> +
> +       return true;
> +}
> +
>  static int cxl_mem_probe(struct device *dev)
>  {
> -       return -EOPNOTSUPP;
> +       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> +       struct cxl_mem *cxlm = cxlmd->cxlm;
> +       struct device *pdev_parent = cxlm->dev->parent;
> +       struct pci_dev *pdev = to_pci_dev(cxlm->dev);

It's not safe to assume that the parent of a cxlmd is a pci device.

> +       struct device *port_dev;
> +
> +       if (!is_cxl_mem_enabled(pdev))
> +               return -ENODEV;

This isn't sufficient, this needs to walk the entire hierarchy, right?

> +
> +       /* TODO: if parent is a switch, this will fail. */

Won't the parent be a switch in all cases? For example, even in QEMU
today the parent of the CXL device is the switch in the host bridge.

# cat /sys/bus/cxl/devices/port1/decoder1.0/devtype
cxl_decoder_switch

> +       port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
> +       if (!port_dev)
> +               return -ENODEV;
> +
> +       return 0;
>  }
>
>  static void cxl_mem_remove(struct device *dev)
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 6931885c83ce..244b99948c40 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -335,29 +335,6 @@ static void cxl_pci_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
>         pci_iounmap(to_pci_dev(cxlm->dev), base);
>  }
>
> -static int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> -{
> -       int pos;
> -
> -       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> -       if (!pos)
> -               return 0;
> -
> -       while (pos) {
> -               u16 vendor, id;
> -
> -               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> -               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> -               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> -                       return pos;
> -
> -               pos = pci_find_next_ext_capability(pdev, pos,
> -                                                  PCI_EXT_CAP_ID_DVSEC);
> -       }
> -
> -       return 0;
> -}
> -
>  static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
>                           struct cxl_register_map *map)
>  {
> diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
> index 8c1a58813816..d6b9978d05b0 100644
> --- a/drivers/cxl/pci.h
> +++ b/drivers/cxl/pci.h
> @@ -11,7 +11,10 @@
>   */
>  #define PCI_DVSEC_HEADER1_LENGTH_MASK  GENMASK(31, 20)
>  #define PCI_DVSEC_VENDOR_ID_CXL                0x1E98
> -#define PCI_DVSEC_ID_CXL               0x0
> +
> +#define PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID   0x0
> +#define PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET      0xC
> +#define   CXL_PCIE_MEM_ENABLE                  BIT(2)
>
>  #define PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID       0x8
>  #define PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET  0xC
> @@ -29,4 +32,6 @@
>
>  #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
>
> +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
> +
>  #endif /* __CXL_PCI_H__ */
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 08/13] cxl/mem: Add memdev as a port
  2021-09-02 19:50 ` [PATCH 08/13] cxl/mem: Add memdev as a port Ben Widawsky
  2021-09-03 15:31   ` Jonathan Cameron
@ 2021-09-10 23:09   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 23:09 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> CXL endpoints contain HDM decoders that are architecturally the same as
> a CXL switch, or a CXL hostbridge. While some restrictions are in place
> for endpoints, they will require the same enumeration logic to determine
> the number and abilities of the HDM decoders.
>
> Utilizing the existing port APIs from cxl_core is the simplest way to
> gain access to the same set of information that switches and hostbridges
> have.

Per the comment a few patches back I think this patch deserves to be
moved before and referenced by the endpoint-decoder patch.

>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c |  5 ++++-
>  drivers/cxl/mem.c      | 10 +++++++++-
>  2 files changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index 56f57302d27b..f26095b40f5c 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -377,7 +377,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>
>         dev = &port->dev;
>         if (parent_port)
> -               rc = dev_set_name(dev, "port%d", port->id);
> +               if (host->type == &cxl_memdev_type)
> +                       rc = dev_set_name(dev, "devport%d", port->id);

While I am certain that a root port will always be at the root, I'm
only 99% convinced that port in a device will never have child-ports,
so I'm inclined that this still be named "portX" and userspace must
consult portX/devtype to determine the port rather than infer it from
the name.

> +               else
> +                       rc = dev_set_name(dev, "port%d", port->id);
>         else
>                 rc = dev_set_name(dev, "root%d", port->id);
>         if (rc)
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index b6dc34d18a86..9d5a3a29cda1 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -63,6 +63,7 @@ static int cxl_mem_probe(struct device *dev)
>         struct device *pdev_parent = cxlm->dev->parent;
>         struct pci_dev *pdev = to_pci_dev(cxlm->dev);
>         struct device *port_dev;
> +       int rc;
>
>         if (!is_cxl_mem_enabled(pdev))
>                 return -ENODEV;
> @@ -72,7 +73,14 @@ static int cxl_mem_probe(struct device *dev)
>         if (!port_dev)
>                 return -ENODEV;
>
> -       return 0;
> +       /* TODO: Obtain component registers */

The agent that registered the memdev should have already enumerated
them for this device. Let's not duplicate that enumeration. I would
hope that this driver could be PCI details free and only operate on
memory-mapped resources.

> +       rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,

I'd prefer this be broken out on multiple lines.

port = devm_cxl_add_port(...);
rc = PTR_ERR_OR_ZERO(port);


> +                                              CXL_RESOURCE_NONE,
> +                                              to_cxl_port(port_dev)));
> +       if (rc)
> +               dev_err(dev, "Unable to add devices upstream port");

Perhaps:

"Failed to register port"

...it will already be clear that it's a device port from the
device-name and driver that will be prepended to this print.


> +
> +       return rc;
>  }
>
>  static void cxl_mem_remove(struct device *dev)
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe
  2021-09-02 19:50 ` [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe Ben Widawsky
@ 2021-09-10 23:12   ` Dan Williams
  2021-09-10 23:45     ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 23:12 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> In order for a memdev to participate in cxl_core's port APIs, the
> physical address of the memdev's component registers is needed. This is
> accomplished by allocating the array of maps in probe so they can be
> used after the memdev is created.

Is this still needed if you append this map information to the memdev directly?

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
  2021-09-02 22:41   ` Ben Widawsky
  2021-09-03 16:14   ` Jonathan Cameron
@ 2021-09-10 23:44   ` Dan Williams
  2 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 23:44 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Component registers are implemented for CXL.mem/cache operations. The
> cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> The driver for managing CXL.mem/cache operations will need the component
> registers mapped and the mapping cannot be shared across two devices.
>
> For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> exception (perhaps others will exist) where it might be desirable to
> have the cxl_pci driver do negotiation. For this case, it probably will
> make sense to create an ephemeral mapping. Further looking, there might
> need to be a cxl_core mechanism to allow arbitrating access to the
> component registers.

My reaction to this is to make a single driver responsible for all
component capabilities, don't try to share. If another agent wants to
interact with those registers it must do so through an API provided by
that driver. In this case this seems to want a cxl_port driver.

>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c    | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/cxl/core/memdev.c | 11 +++++++----
>  drivers/cxl/core/regs.c   |  6 +++---
>  drivers/cxl/cxl.h         |  4 ++++
>  drivers/cxl/cxlmem.h      |  4 +++-
>  drivers/cxl/mem.c         |  3 +--
>  drivers/cxl/pci.c         | 19 +++++++++++++++++--
>  7 files changed, 73 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index f26095b40f5c..01b6fa8373e4 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -310,6 +310,37 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
>         return devm_add_action_or_reset(host, cxl_unlink_uport, port);
>  }
>
> +static int cxl_port_map_component_registers(struct cxl_port *port)
> +{
> +       struct cxl_register_map map;
> +       struct cxl_component_reg_map *comp_map = &map.component_map;
> +       void __iomem *crb;
> +
> +       if (port->component_reg_phys == CXL_RESOURCE_NONE)
> +               return 0;
> +
> +       crb = devm_cxl_iomap_block(&port->dev,
> +                                  port->component_reg_phys,
> +                                  /* CXL_COMPONENT_REG_BLOCK_SIZE */ SZ_64K);

Unless this is being called by a driver for @port->dev then this leaks
the mapping longer than is necessary. The devres_release_all()
callback is triggered after device->driver->remove() and in
device_release().

> +       if (IS_ERR(crb))
> +               return PTR_ERR(crb);
> +
> +       if (!crb) {
> +               dev_err(&port->dev, "No component registers mapped\n");
> +               return -ENXIO;
> +       }
> +
> +       cxl_probe_component_regs(&port->dev, crb, comp_map);
> +       if (!comp_map->hdm_decoder.valid) {
> +               dev_err(&port->dev, "HDM decoder registers invalid\n");
> +               return -ENXIO;
> +       }
> +
> +       port->regs.hdm_decoder = crb + comp_map->hdm_decoder.offset;
> +
> +       return 0;
> +}
> +
>  static struct cxl_port *cxl_port_alloc(struct device *uport,
>                                        resource_size_t component_reg_phys,
>                                        struct cxl_port *parent_port)
> @@ -398,6 +429,13 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>         if (rc)
>                 return ERR_PTR(rc);
>
> +       /* Platform "switch" has no parent port or component registers */
> +       if (parent_port) {
> +               rc = cxl_port_map_component_registers(port);

I don't think this belongs here. Lets keep all register mapping in
drivers, and add a port driver for component register services while
the port is enabled.

> +               if (rc)
> +                       return ERR_PTR(rc);
> +       }
> +
>         return port;
>
>  err:
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 0068b5ff5f3e..85fe42abd29b 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -185,7 +185,8 @@ static void cxl_memdev_unregister(void *_cxlmd)
>  }
>
>  static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
> -                                          const struct file_operations *fops)
> +                                          const struct file_operations *fops,
> +                                          unsigned long component_reg_phys)
>  {
>         struct cxl_memdev *cxlmd;
>         struct device *dev;
> @@ -200,6 +201,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
>         if (rc < 0)
>                 goto err;
>         cxlmd->id = rc;
> +       cxlmd->component_reg_phys = component_reg_phys;
>
>         dev = &cxlmd->dev;
>         device_initialize(dev);
> @@ -275,15 +277,16 @@ static const struct file_operations cxl_memdev_fops = {
>         .llseek = noop_llseek,
>  };
>
> -struct cxl_memdev *
> -devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm)
> +struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> +                                      struct cxl_mem *cxlm,
> +                                      unsigned long component_reg_phys)
>  {
>         struct cxl_memdev *cxlmd;
>         struct device *dev;
>         struct cdev *cdev;
>         int rc;
>
> -       cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
> +       cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops, component_reg_phys);

Apologies, the dvsec walking in the previous patch made me think you
were going to re-discover component registers in the cxl_memdev
driver. Ignore that implication, this looks good.

>         if (IS_ERR(cxlmd))
>                 return cxlmd;
>
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 8535a7b94f28..4ba75fb6779f 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -145,9 +145,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
>  }
>  EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
>
> -static void __iomem *devm_cxl_iomap_block(struct device *dev,
> -                                         resource_size_t addr,
> -                                         resource_size_t length)
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +                                  resource_size_t length)
>  {
>         void __iomem *ret_val;
>         struct resource *res;
> @@ -166,6 +165,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,
>
>         return ret_val;
>  }
> +EXPORT_SYMBOL_GPL(devm_cxl_iomap_block);
>
>  int cxl_map_component_regs(struct pci_dev *pdev,
>                            struct cxl_component_regs *regs,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index a168520d741b..4585d03a0a67 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -149,6 +149,8 @@ struct cxl_register_map {
>         };
>  };
>
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> +                                  resource_size_t length);
>  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,
> @@ -252,6 +254,7 @@ struct cxl_walk_context {
>   * @dports: cxl_dport instances referenced by decoders
>   * @decoder_ida: allocator for decoder ids
>   * @component_reg_phys: component register capability base address (optional)
> + * @regs: Mapped version of @component_reg_phys
>   */
>  struct cxl_port {
>         struct device dev;
> @@ -260,6 +263,7 @@ struct cxl_port {
>         struct list_head dports;
>         struct ida decoder_ida;
>         resource_size_t component_reg_phys;
> +       struct cxl_component_regs regs;
>  };
>
>  /**
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 88264204c4b9..f94624e43b2e 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -41,6 +41,7 @@ struct cxl_memdev {
>         struct cdev cdev;
>         struct cxl_mem *cxlm;
>         int id;
> +       unsigned long component_reg_phys;
>  };
>
>  static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> @@ -49,7 +50,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
>  }
>
>  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> -                                      struct cxl_mem *cxlm);
> +                                      struct cxl_mem *cxlm,
> +                                      unsigned long component_reg_phys);
>
>  bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
>
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 9d5a3a29cda1..aba9a07d519f 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -73,9 +73,8 @@ static int cxl_mem_probe(struct device *dev)
>         if (!port_dev)
>                 return -ENODEV;
>
> -       /* TODO: Obtain component registers */
>         rc = PTR_ERR_OR_ZERO(devm_cxl_add_port(&cxlmd->dev, &cxlmd->dev,
> -                                              CXL_RESOURCE_NONE,
> +                                              cxlmd->component_reg_phys,
>                                                to_cxl_port(port_dev)));
>         if (rc)
>                 dev_err(dev, "Unable to add devices upstream port");
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index e4b3549c4580..258190febb5a 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -382,8 +382,12 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
>
>         switch (map->reg_type) {
>         case CXL_REGLOC_RBI_COMPONENT:
> +#ifndef CONFIG_CXL_MEM

Argh, please no ifdef outside of header. Not even sure why this is
needed. cxl_pci has no use for the component registers, so I would
just expect this case to be deleted altogether.

In fact I don't even think we need to use a struct_group() anymore
with a separate cxl_port driver.

>                 cxl_map_component_regs(pdev, &cxlm->regs.component, map);
>                 dev_dbg(dev, "Mapping component registers...\n");
> +#else
> +               dev_dbg(dev, "Component registers not mapped for %s\n", KBUILD_MODNAME);
> +#endif
>                 break;
>         case CXL_REGLOC_RBI_MEMDEV:
>                 cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
> @@ -493,10 +497,11 @@ static int cxl_pci_setup_regs(struct cxl_mem *cxlm, struct cxl_register_map maps
>
>  static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
> +       unsigned long component_reg_phys = CXL_RESOURCE_NONE;
>         struct cxl_register_map maps[CXL_REGLOC_RBI_TYPES];
>         struct cxl_memdev *cxlmd;
>         struct cxl_mem *cxlm;
> -       int rc;
> +       int rc, i;
>
>         /*
>          * Double check the anonymous union trickery in struct cxl_regs
> @@ -533,7 +538,17 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>         if (rc)
>                 return rc;
>
> -       cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm);
> +       for (i = 0; i < ARRAY_SIZE(maps); i++) {
> +               struct cxl_register_map *map = &maps[i];
> +
> +               if (map->reg_type != CXL_REGLOC_RBI_COMPONENT)
> +                       continue;
> +
> +               component_reg_phys = pci_resource_start(pdev, map->barno) +
> +                                    map->block_offset;
> +       }
> +
> +       cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, component_reg_phys);

Perhaps squash this patch with the one that keeps the register map
around longer, I got confused by the lack of diff context about where
devm_cxl_add_memdev() could intercept pulling out the information it
needs.

>         if (IS_ERR(cxlmd))
>                 return PTR_ERR(cxlmd);
>
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe
  2021-09-10 23:12   ` Dan Williams
@ 2021-09-10 23:45     ` Dan Williams
  0 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-10 23:45 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Fri, Sep 10, 2021 at 4:12 PM Dan Williams <dan.j.williams@intel.com> wrote:
>
> On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > In order for a memdev to participate in cxl_core's port APIs, the
> > physical address of the memdev's component registers is needed. This is
> > accomplished by allocating the array of maps in probe so they can be
> > used after the memdev is created.
>
> Is this still needed if you append this map information to the memdev directly?

Disregard. The lack of diff context and my leaky brain made me forget
that devm_cxl_add_memdev() is at the bottom of cxl_pci_probe(). I
would just squash this with the patch that actually needs it.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-03 16:14   ` Jonathan Cameron
@ 2021-09-10 23:52     ` Dan Williams
  2021-09-13  8:29       ` Jonathan Cameron
  0 siblings, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-10 23:52 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Ben Widawsky, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On Fri, Sep 3, 2021 at 9:14 AM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Thu, 2 Sep 2021 12:50:14 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> > Component registers are implemented for CXL.mem/cache operations. The
> > cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> > The driver for managing CXL.mem/cache operations will need the component
> > registers mapped and the mapping cannot be shared across two devices.
> >
> > For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> > exception (perhaps others will exist) where it might be desirable to
> > have the cxl_pci driver do negotiation. For this case, it probably will
> > make sense to create an ephemeral mapping. Further looking, there might
> > need to be a cxl_core mechanism to allow arbitrating access to the
> > component registers.
>
>
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>
> As you predicted I don't like this. Needs some thought on how to get
> around the mapping games though and it's Friday afternoon so I'm not
> going to offer any concrete answers...
>
> Not totally obvious to me where RAS will be handled as well.
> I think we definitely need an arbitration mechanism here.

Poison consumption is handled via typical memory_failure(). Event
record and event interrupts can be handled by the PCI driver
potentially notifying the memdev driver if necessary. Port specific
error handling (RAS component registers) can be handled by the port
driver.

> Wouldn't it have been nice if all these capabilities had been nicely
> padded so we could map them individually.  Oh well!
> Gut feeling is this will only get worse for future versions of the spec
> so we should assume there will be lots of stuff shoved in here.

I'd prefer to run away from arbitration and towards sub-drivers
providing services with distinct lines of ownership. In the rare cases
where a driver can not act independently there should be a well
defined driver API to request help from the register block owner, not
pass around access to the register block.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 11/13] cxl/core: Convert decoder range to resource
  2021-09-02 19:50 ` [PATCH 11/13] cxl/core: Convert decoder range to resource Ben Widawsky
  2021-09-03 16:16   ` Jonathan Cameron
@ 2021-09-11  0:59   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-11  0:59 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Regions will use the resource API in order to help manage allocated
> space. As regions are children of the decoder, it makes sense that the
> parent host the main resource to be suballocated by the region.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/acpi.c     | 12 ++++--------
>  drivers/cxl/core/bus.c |  4 ++--
>  drivers/cxl/cxl.h      |  4 ++--
>  3 files changed, 8 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index fd14094bdb3f..26691313d716 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -125,10 +125,9 @@ static void cxl_add_cfmws_decoders(struct device *dev,
>
>                 cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
>                 cxld->target_type = CXL_DECODER_EXPANDER;
> -               cxld->range = (struct range) {
> -                       .start = cfmws->base_hpa,
> -                       .end = cfmws->base_hpa + cfmws->window_size - 1,
> -               };
> +               cxld->res = (struct resource)DEFINE_RES_MEM_NAMED(cfmws->base_hpa,
> +                                                                 cfmws->window_size,
> +                                                                 "cfmws");

I like this direction, but it's unfortunate to carry the bloat of
'struct resource' in all decoders when only the top-level needs it (as
far as I can see). I think it will be handy to have a global resource
tree available for address translation service to the rest of the OS
where resource providers / holders can be looked up by name in a
cxl-specific memory resource tree. I.e. how about something like:

diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
index be787685b13e..e64939f1b07d 100644
--- a/drivers/cxl/core/bus.c
+++ b/drivers/cxl/core/bus.c
@@ -26,6 +26,8 @@

 static DEFINE_IDA(cxl_port_ida);

+static struct resource cxlmem_resource = DEFINE_RES_MEM_NAMED(0, -1,
"CXL mem");
+
 static ssize_t devtype_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
@@ -180,6 +182,9 @@ static void cxl_decoder_release(struct device *dev)
        struct cxl_decoder *cxld = to_cxl_decoder(dev);
        struct cxl_port *port = to_cxl_port(dev->parent);

+       if (cxld->res)
+               remove_resource(cxld->res);
+       kfree(cxld->res);
        ida_free(&port->decoder_ida, cxld->id);
        kfree(cxld);
 }
@@ -545,6 +550,24 @@ int cxl_decoder_add(struct device *host, struct
cxl_decoder *cxld,
        if (rc)
                return rc;

+       if (dev->type == &cxl_decoder_root_type) {
+               struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
+
+               if (!res)
+                       return -ENOMEM;
+               *res = (struct resource) DEFINE_RES_MEM_NAMED(
+                       cxld->range.start,
+                       cxld->range.end,
+                       dev_name(dev)
+               );
+
+               rc = insert_resource(&cxlmem_resource, res);
+               if (rc) {
+                       kfree(res);
+                       return rc;
+               }
+       }
+
        return device_add(dev);
 }
 EXPORT_SYMBOL_GPL(cxl_decoder_add);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 6c7a7e9af0d4..7d0d218d9883 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -190,7 +190,8 @@ enum cxl_decoder_type {
  * struct cxl_decoder - CXL address range decode configuration
  * @dev: this decoder's device
  * @id: kernel device name id
- * @range: address range considered by this decoder
+ * @range: current address range considered by this decoder
+ * @res: top level CXL mem resource (root decoder only)
  * @interleave_ways: number of cxl_dports in this decode
  * @interleave_granularity: data stride per dport
  * @target_type: accelerator vs expander (type2 vs type3) selector
@@ -202,6 +203,7 @@ struct cxl_decoder {
        struct device dev;
        int id;
        struct range range;
+       struct resource *res;
        int interleave_ways;
        int interleave_granularity;
        enum cxl_decoder_type target_type;

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders
  2021-09-02 19:50 ` [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders Ben Widawsky
  2021-09-03 17:43   ` Jonathan Cameron
@ 2021-09-11  1:13   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-11  1:13 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> As of the CXL 2.0 specification, every port will have between 1 and 10
> HDM decoders available in hardware. These exist in the endpoint, switch,
> and top level hostbridges. HDM decoders are required for configuration
> CXL regions, and therefore enumerating them is an important first step.
>
> As an example, the below has 4 decoders, a top level CFMWS decoder
> (0.0), a single decoder in a single host bridge (1.0), and two devices
> each with 1 decoder (2.0 and 3.0)
>
> ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> ├── decoder1.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/decoder1.0
> ├── decoder2.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport2/decoder2.0
> ├── decoder3.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport3/decoder3.0
>
> Additionally, attributes are added for a port:
>
> /sys/bus/cxl/devices/port1
> ├── active_decoders
> ├── decoder_count
> ├── decoder_enabled
> ├── max_target_count
> ...
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/core/bus.c | 161 ++++++++++++++++++++++++++++++++++++++++-
>  drivers/cxl/cxl.h      |  54 ++++++++++++--
>  2 files changed, 209 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> index d056dbd794a4..b75e42965e89 100644
> --- a/drivers/cxl/core/bus.c
> +++ b/drivers/cxl/core/bus.c
> @@ -43,6 +43,15 @@ struct attribute_group cxl_base_attribute_group = {
>         .attrs = cxl_base_attributes,
>  };
>
> +static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
> +                           char *buf)
> +{
> +       struct cxl_decoder *cxld = to_cxl_decoder(dev);
> +
> +       return sysfs_emit(buf, "%d\n", !!cxld->decoder_enabled);

In hardware there is a global disable for a set of decoders, and the
expectation that a decoder programmed to zero size is disabled. I'd
rather just have size be the indicator than a decoder_enabled flag.

> +}
> +static DEVICE_ATTR_RO(enabled);
> +
>  static ssize_t start_show(struct device *dev, struct device_attribute *attr,
>                           char *buf)
>  {
> @@ -130,6 +139,7 @@ static ssize_t target_list_show(struct device *dev,
>  static DEVICE_ATTR_RO(target_list);
>
>  static struct attribute *cxl_decoder_base_attrs[] = {
> +       &dev_attr_enabled.attr,
>         &dev_attr_start.attr,
>         &dev_attr_size.attr,
>         &dev_attr_locked.attr,
> @@ -249,8 +259,48 @@ static void cxl_port_release(struct device *dev)
>         kfree(port);
>  }
>
> +static ssize_t active_decoders_show(struct device *dev,
> +                                   struct device_attribute *attr, char *buf)
> +{
> +       struct cxl_port *port = to_cxl_port(dev);
> +
> +       return sysfs_emit(buf, "%*pbl\n", port->decoder_cap.count,
> +                         port->used_decoders);

What would userspace do with this ABI? A Documentation/ABI/ entry for
these would help answer that.

> +}
> +static DEVICE_ATTR_RO(active_decoders);
> +
> +static ssize_t decoder_count_show(struct device *dev,
> +                                 struct device_attribute *attr, char *buf)
> +{
> +       struct cxl_port *port = to_cxl_port(dev);
> +
> +       return sysfs_emit(buf, "%d\n", port->decoder_cap.count);

Similar question here, can't userspace already count them by walking
the directory tree?

> +}
> +static DEVICE_ATTR_RO(decoder_count);
> +
> +static ssize_t max_target_count_show(struct device *dev,
> +                                    struct device_attribute *attr, char *buf)
> +{
> +       struct cxl_port *port = to_cxl_port(dev);
> +
> +       return sysfs_emit(buf, "%d\n", port->decoder_cap.target_count);

Just count the entries in target_list?

> +}
> +static DEVICE_ATTR_RO(max_target_count);
> +
> +static struct attribute *cxl_port_caps_attributes[] = {
> +       &dev_attr_active_decoders.attr,
> +       &dev_attr_decoder_count.attr,
> +       &dev_attr_max_target_count.attr,
> +       NULL,
> +};
> +
> +struct attribute_group cxl_port_attribute_group = {
> +       .attrs = cxl_port_caps_attributes,
> +};
> +
>  static const struct attribute_group *cxl_port_attribute_groups[] = {
>         &cxl_base_attribute_group,
> +       &cxl_port_attribute_group,
>         NULL,
>  };
>
> @@ -341,6 +391,107 @@ static int cxl_port_map_component_registers(struct cxl_port *port)
>         return 0;
>  }
>
> +static int port_populate_caps(struct cxl_port *port)
> +{
> +       void __iomem *hdm_decoder = port->regs.hdm_decoder;
> +       u32 hdm_cap;
> +
> +       hdm_cap = readl(hdm_decoder + CXL_HDM_DECODER_CAP_OFFSET);
> +
> +       port->used_decoders = devm_bitmap_zalloc(&port->dev,
> +                                                cxl_hdm_decoder_count(hdm_cap),
> +                                                GFP_KERNEL);
> +       if (!port->used_decoders)
> +               return -ENOMEM;
> +
> +       port->decoder_cap.count = cxl_hdm_decoder_count(hdm_cap);
> +       port->decoder_cap.target_count =
> +               FIELD_GET(CXL_HDM_DECODER_TARGET_COUNT_MASK, hdm_cap);
> +       port->decoder_cap.interleave11_8 =
> +               FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_11_8, hdm_cap);
> +       port->decoder_cap.interleave14_12 =
> +               FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap);
> +
> +       return 0;
> +}
> +
> +static int cxl_port_enumerate_hdm_decoders(struct device *host,
> +                                          struct cxl_port *port)
> +{
> +       void __iomem *hdm_decoder = port->regs.hdm_decoder;
> +       u32 hdm_ctrl;
> +       int i, rc = 0;
> +
> +       rc = port_populate_caps(port);
> +       if (rc)
> +               return rc;
> +
> +       if (port->decoder_cap.count == 0) {
> +               dev_warn(host, "Found no HDM decoders\n");
> +               return -ENODEV;
> +       }
> +
> +       for (i = 0; i < port->decoder_cap.count; i++) {
> +               enum cxl_decoder_type type = CXL_DECODER_EXPANDER;
> +               struct resource res = DEFINE_RES_MEM(0, 0);
> +               struct cxl_decoder *cxld;
> +               int iw = 0, ig = 0;
> +               u32 ctrl;
> +
> +               cxld = cxl_decoder_alloc(port, is_endpoint_decoder(host) ? 0 :
> +                                        port->decoder_cap.target_count);
> +               if (IS_ERR(cxld)) {
> +                       dev_warn(host, "Failed to allocate the decoder\n");
> +                       return PTR_ERR(cxld);
> +               }
> +
> +               ctrl = readl(hdm_decoder + CXL_HDM_DECODER0_CTRL_OFFSET(i));
> +               cxld->decoder_enabled =
> +                       !!FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl);
> +               /* If the decoder is already active, parse info */
> +               if (cxld->decoder_enabled) {
> +                       set_bit(i, port->used_decoders);
> +                       iw = cxl_hdm_decoder_iw(ctrl);
> +                       ig = cxl_hdm_decoder_ig(ctrl);
> +                       if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0)
> +                               type = CXL_DECODER_ACCELERATOR;
> +                       res.start = readl(hdm_decoder +
> +                                         CXL_HDM_DECODER0_BASE_LOW_OFFSET(i));
> +                       res.start |=
> +                               (u64)readl(hdm_decoder +
> +                                          CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i))
> +                               << 32;
> +               }
> +
> +               cxld->target_type = type;
> +               cxld->res = res;
> +               cxld->interleave_ways = iw;
> +               cxld->interleave_granularity = ig;
> +
> +               rc = cxl_decoder_add(host, cxld, NULL);
> +               if (rc) {
> +                       dev_warn(host, "Failed to add decoder (%d)\n", rc);
> +                       kfree(cxld);
> +                       goto out;
> +               }
> +       }
> +
> +       /*
> +        * Enable CXL.mem decoding via MMIO for endpoint devices
> +        *
> +        * TODO: If a memory device was configured to participate in a region by
> +        * system firmware via DVSEC, this will break that region.
> +        */
> +       if (is_endpoint_decoder(host)) {
> +               hdm_ctrl = readl(hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
> +               writel(hdm_ctrl | CXL_HDM_DECODER_ENABLE,
> +                      hdm_decoder + CXL_HDM_DECODER_CTRL_OFFSET);
> +       }
> +
> +out:
> +       return rc;
> +}
> +
>  static struct cxl_port *cxl_port_alloc(struct device *uport,
>                                        resource_size_t component_reg_phys,
>                                        struct cxl_port *parent_port)
> @@ -432,8 +583,16 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
>         /* Platform "switch" has no parent port or component registers */
>         if (parent_port) {
>                 rc = cxl_port_map_component_registers(port);
> -               if (rc)
> +               if (rc) {
> +                       dev_err(host, "Failed to map component registers\n");
>                         return ERR_PTR(rc);
> +               }
> +
> +               rc = cxl_port_enumerate_hdm_decoders(host, port);
> +               if (rc) {
> +                       dev_err(host, "Failed to enumerate HDM decoders\n");
> +                       return ERR_PTR(rc);
> +               }

...looks more and more like cxl_port_probe().

>         }
>
>         return port;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index e610fa9dd6c8..6759fe097e12 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -36,11 +36,19 @@
>  #define CXL_HDM_DECODER_CAP_OFFSET 0x0
>  #define   CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
>  #define   CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
> -#define CXL_HDM_DECODER0_BASE_LOW_OFFSET 0x10
> -#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET 0x14
> -#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET 0x18
> -#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET 0x1c
> -#define CXL_HDM_DECODER0_CTRL_OFFSET 0x20
> +#define   CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
> +#define   CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
> +#define CXL_HDM_DECODER_CTRL_OFFSET 0x0
> +#define   CXL_HDM_DECODER_ENABLE BIT(1)
> +#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x10 + (i) * 0x20)
> +#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i) (0x14 + (i) * 0x20)
> +#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i) (0x18 + (i) * 0x20)
> +#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i) (0x1c + (i) * 0x20)
> +#define CXL_HDM_DECODER0_CTRL_OFFSET(i) (0x20 + (i) * 0x20)
> +#define   CXL_HDM_DECODER0_CTRL_IG_MASK GENMASK(3, 0)
> +#define   CXL_HDM_DECODER0_CTRL_IW_MASK GENMASK(7, 4)
> +#define   CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
> +#define   CXL_HDM_DECODER0_CTRL_TYPE BIT(12)
>
>  static inline int cxl_hdm_decoder_count(u32 cap_hdr)
>  {
> @@ -49,6 +57,20 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr)
>         return val ? val * 2 : 1;
>  }
>
> +static inline int cxl_hdm_decoder_ig(u32 ctrl)
> +{
> +       int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl);
> +
> +       return 8 + val;
> +}
> +
> +static inline int cxl_hdm_decoder_iw(u32 ctrl)
> +{
> +       int val = FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl);
> +
> +       return 1 << val;
> +}
> +
>  /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
>  #define CXLDEV_CAP_ARRAY_OFFSET 0x0
>  #define   CXLDEV_CAP_ARRAY_CAP_ID 0
> @@ -188,6 +210,12 @@ enum cxl_decoder_type {
>   */
>  #define CXL_DECODER_MAX_INTERLEAVE 16
>
> +/*
> + * Current specification goes up to 10 double that seems a reasonable
> + * software max for the foreseeable future
> + */
> +#define CXL_DECODER_MAX_COUNT 20
> +
>  /**
>   * struct cxl_decoder - CXL address range decode configuration
>   * @dev: this decoder's device
> @@ -197,6 +225,7 @@ enum cxl_decoder_type {
>   * @interleave_granularity: data stride per dport
>   * @target_type: accelerator vs expander (type2 vs type3) selector
>   * @flags: memory type capabilities and locking
> + * @decoder_enabled: Is this decoder currently decoding
>   * @nr_targets: number of elements in @target
>   * @target: active ordered target list in current decoder configuration
>   */
> @@ -208,6 +237,7 @@ struct cxl_decoder {
>         int interleave_granularity;
>         enum cxl_decoder_type target_type;
>         unsigned long flags;
> +       bool decoder_enabled;
>         int nr_targets;
>         struct cxl_dport *target[];
>  };
> @@ -255,6 +285,12 @@ struct cxl_walk_context {
>   * @decoder_ida: allocator for decoder ids
>   * @component_reg_phys: component register capability base address (optional)
>   * @regs: Mapped version of @component_reg_phys
> + * @used_decoders: Bitmap of currently active decoders for the port
> + * @decoder_cap: Capabilities of all decoders contained by the port
> + * @decoder_cap.count: Count of HDM decoders for the port
> + * @decoder_cap.target_count: Max number of interleaved downstream ports
> + * @decoder_cap.interleave11_8: Are address bits 11-8 available for interleave
> + * @decoder_cap.interleave14_12: Are address bits 14-12 available for interleave
>   */
>  struct cxl_port {
>         struct device dev;
> @@ -264,6 +300,14 @@ struct cxl_port {
>         struct ida decoder_ida;
>         resource_size_t component_reg_phys;
>         struct cxl_component_regs regs;
> +
> +       unsigned long *used_decoders;
> +       struct {
> +               int count;
> +               int target_count;
> +               bool interleave11_8;
> +               bool interleave14_12;
> +       } decoder_cap;

This looks like something that would come from dev_get_drvdata(&port->dev).

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders
  2021-09-03 17:43   ` Jonathan Cameron
@ 2021-09-11  1:37     ` Dan Williams
  0 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-11  1:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Ben Widawsky, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On Fri, Sep 3, 2021 at 10:44 AM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Thu, 2 Sep 2021 12:50:16 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> > As of the CXL 2.0 specification, every port will have between 1 and 10
> > HDM decoders available in hardware. These exist in the endpoint, switch,
> > and top level hostbridges. HDM decoders are required for configuration
> > CXL regions, and therefore enumerating them is an important first step.
> >
> > As an example, the below has 4 decoders, a top level CFMWS decoder
> > (0.0), a single decoder in a single host bridge (1.0), and two devices
> > each with 1 decoder (2.0 and 3.0)
> >
> > ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> > ├── decoder1.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/decoder1.0
> > ├── decoder2.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport2/decoder2.0
> > ├── decoder3.0 -> ../../../devices/platform/ACPI0017:00/root0/port1/devport3/decoder3.0
> >
> > Additionally, attributes are added for a port:
> >
> > /sys/bus/cxl/devices/port1
> > ├── active_decoders
> > ├── decoder_count
> > ├── decoder_enabled
> > ├── max_target_count
> > ...
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>
> Documentation/ABI/testing/sysfs-bus-cxl  needs updating.
>
> Various minor things inline.
>
>
> > ---
> >  drivers/cxl/core/bus.c | 161 ++++++++++++++++++++++++++++++++++++++++-
> >  drivers/cxl/cxl.h      |  54 ++++++++++++--
> >  2 files changed, 209 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index d056dbd794a4..b75e42965e89 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
[..]
> > +             ctrl = readl(hdm_decoder + CXL_HDM_DECODER0_CTRL_OFFSET(i));
> > +             cxld->decoder_enabled =
> > +                     !!FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl);
> > +             /* If the decoder is already active, parse info */
> > +             if (cxld->decoder_enabled) {
> > +                     set_bit(i, port->used_decoders);
> > +                     iw = cxl_hdm_decoder_iw(ctrl);
> > +                     ig = cxl_hdm_decoder_ig(ctrl);
> > +                     if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0)
> > +                             type = CXL_DECODER_ACCELERATOR;
> > +                     res.start = readl(hdm_decoder +
> > +                                       CXL_HDM_DECODER0_BASE_LOW_OFFSET(i));
> > +                     res.start |=
> > +                             (u64)readl(hdm_decoder +
> > +                                        CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i))
> > +                             << 32;
>
> Perhaps worth thinking about exposing if the decoder is locked as well?
> Might be good to let userspace know that..

In fact...

What:           /sys/bus/cxl/devices/decoderX.Y/locked
Date:           June, 2021
KernelVersion:  v5.14
Contact:        linux-cxl@vger.kernel.org
Description:
                CXL HDM decoders have the capability to lock the configuration
                until the next device reset. For decoders of devtype
                "cxl_decoder_root" there is no standard facility to unlock them.
                For decoders of devtype "cxl_decoder_switch" a secondary bus
                reset, of the PCIe bridge that provides the bus for this
                decoders uport, unlocks / resets the decoder.


>
> > +             }
> > +
> > +             cxld->target_type = type;
> > +             cxld->res = res;
> > +             cxld->interleave_ways = iw;
> > +             cxld->interleave_granularity = ig;
> > +
> > +             rc = cxl_decoder_add(host, cxld, NULL);
> > +             if (rc) {
> > +                     dev_warn(host, "Failed to add decoder (%d)\n", rc);
> > +                     kfree(cxld);
> > +                     goto out;
> > +             }
> > +     }
> > +
> > +     /*
> > +      * Enable CXL.mem decoding via MMIO for endpoint devices
> > +      *
> > +      * TODO: If a memory device was configured to participate in a region by
> > +      * system firmware via DVSEC, this will break that region.

Spec reference please (8.2.5.12.2 CXL HDM Decoder Global Control
Register) . Also the spec calls this "HDM Base registers in DVSEC ID
0".

>
> That seems to me like fatal for this patch set until fixed.
> I'm not sure I understand why it will break a region though as I'd assume it
> would be already on?

The spec recommends that implementations set the HDM Base registers to
match the CXL HDM Decoder 0 value. The expectation is that platform
firmware is locking any HDM decoders for anything that it puts into
the platform firmware memory map. So I think a reasonable heuristic
here is decline to touch / support any device with HDM Base registers
pointing at a live entry in the system memory map as an indication
that the platform firmware is not prepared to interoperate with a CXL
2.0+ aware OS. CXL 2.0+ capable platform firmware should always use
HDM Decoders.

[..]
> >  /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
> >  #define CXLDEV_CAP_ARRAY_OFFSET 0x0
> >  #define   CXLDEV_CAP_ARRAY_CAP_ID 0
> > @@ -188,6 +210,12 @@ enum cxl_decoder_type {
> >   */
> >  #define CXL_DECODER_MAX_INTERLEAVE 16
> >
> > +/*
> > + * Current specification goes up to 10 double that seems a reasonable
> > + * software max for the foreseeable future
>
> I'd stick to 10 until we have evidence otherwise... If you happen to
> have a mysterious strong reason to go with 20 though I'm not that fussed
> but I suspect others may have equally strong reasons to pick another number ;)

That's a good point. This is different from max interleave. More
decoders significantly increases the operational complexity of
managing the configuration. If someone thinks they need more than 10,
I would beg to differ, and it helps that Linux pushes back against
that decoder creep by default until someone can come up with an
exceedingly good reason.

>
> > + */
> > +#define CXL_DECODER_MAX_COUNT 20
> > +
> >  /**
> >   * struct cxl_decoder - CXL address range decode configuration
> >   * @dev: this decoder's device
> > @@ -197,6 +225,7 @@ enum cxl_decoder_type {
> >   * @interleave_granularity: data stride per dport
>
> Is it currently documented anywhere that ig is in 2**(ig) bytes?
> Might be worth adding if not.

This structure field would carry the nominal value, not the encoded one, right?

>
> >   * @target_type: accelerator vs expander (type2 vs type3) selector
> >   * @flags: memory type capabilities and locking
> > + * @decoder_enabled: Is this decoder currently decoding
> >   * @nr_targets: number of elements in @target
> >   * @target: active ordered target list in current decoder configuration
> >   */
> > @@ -208,6 +237,7 @@ struct cxl_decoder {
> >       int interleave_granularity;
> >       enum cxl_decoder_type target_type;
> >       unsigned long flags;
> > +     bool decoder_enabled;
> >       int nr_targets;
> >       struct cxl_dport *target[];
> >  };
> > @@ -255,6 +285,12 @@ struct cxl_walk_context {
> >   * @decoder_ida: allocator for decoder ids
> >   * @component_reg_phys: component register capability base address (optional)
> >   * @regs: Mapped version of @component_reg_phys
> > + * @used_decoders: Bitmap of currently active decoders for the port
> > + * @decoder_cap: Capabilities of all decoders contained by the port
> > + * @decoder_cap.count: Count of HDM decoders for the port
> > + * @decoder_cap.target_count: Max number of interleaved downstream ports
> > + * @decoder_cap.interleave11_8: Are address bits 11-8 available for interleave
> > + * @decoder_cap.interleave14_12: Are address bits 14-12 available for interleave
> >   */
> >  struct cxl_port {
> >       struct device dev;
> > @@ -264,6 +300,14 @@ struct cxl_port {
> >       struct ida decoder_ida;
> >       resource_size_t component_reg_phys;
> >       struct cxl_component_regs regs;
> > +
> > +     unsigned long *used_decoders;
>
> Given it's not huge use appropriate macro to allocate
> it directly here with the maximum of 10.
> DECLARE_BITMAP() in similar fashion to patch 19 in Dan's
> recent series.
>
> https://lore.kernel.org/all/162982122744.1124374.6742215706893563515.stgit@dwillia2-desk3.amr.corp.intel.com/
>
> (I'm liking lore having "all" now !)

Mmmm.

>
> > +     struct {
> > +             int count;
> > +             int target_count;
> > +             bool interleave11_8;
> > +             bool interleave14_12;
> > +     } decoder_cap;
> >  };
> >
> >  /**
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops
  2021-09-10 18:51   ` Dan Williams
@ 2021-09-11 17:25     ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-11 17:25 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-10 11:51:14, Dan Williams wrote:
> On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > Since the code to add decoders for switches and endpoints is on the
> > horizon, document the new interfaces that will be consumed by them.
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/core/bus.c | 28 ++++++++++++++++++++++++++++
> >  1 file changed, 28 insertions(+)
> >
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 3991ac231c3e..9d98dd50d424 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -453,6 +453,19 @@ int cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id,
> >  }
> >  EXPORT_SYMBOL_GPL(cxl_add_dport);
> >
> > +/**
> > + * cxl_decoder_alloc - Allocate a new CXL decoder
> > + * @port: owning port of this decoder
> > + * @nr_targets: downstream targets accessible by this decoder
> > + *
> > + * A port should contain one or more decoders. Each of those decoders enable
> > + * some address space for CXL.mem utilization. Therefore, it is logical to
> 
> I think a "therefore it is logical" statement is changelog fodder.
> Once the code is in the kernel it does not need to keep justifying its
> existence.

I agree. This is more appropriate as a commit message.

> 
> > + * allocate decoders while enumerating a port. While >= 1 is defined by the CXL
> > + * specification, due to error conditions it is possible that a port may have 0
> > + * decoders.
> 
> This comment feels out of place. Why does cxl_decoder_alloc() care how
> many decoders a port has? I would expect this comment on a cxl_port
> api that is trying to walk decoders.
> 

I partially agree. The function implementation cares simply for heap allocation
and this detail shouldn't be a part of kdocs. However, as a public API in core,
I think it's warranted to mention cases which might not immediately be obvious.
The main purpose was to change this text when adding endpoints. I didn't
actually end up doing that unfortunately. As such, I think I will move this bit
to the description of nr_targets above.

> > + *
> > + * Return: A new cxl decoder which wants to be added with cxl_decoder_add()
> 
> s/which wants to be added/to be registered by/
> 
> > + */
> >  struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >  {
> >         struct cxl_decoder *cxld;
> > @@ -491,6 +504,21 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >  }
> >  EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
> >
> > +/**
> > + * cxl_decoder_add - Add a decoder with targets
> > + * @host: The containing struct device. This is typically the PCI device that is
> > + *        CXL capable
> 
> No, this is the device doing the enumeration. After the devm removal
> for decoder creation it's now only being used to print a debug
> message. Do you have another use for it? Perhaps it should just be
> deleted. The new cxl_decoder_autoremove() handles what @host was used
> for previously.

I have no use for it beyond what's there. Looks like it should be dropped to me
as well. I'll resend with that removal.

> 
> > + * @cxld: The cxl decoder allocated by cxl_decoder_alloc()
> > + * @target_map: A list of downstream ports that this decoder can direct memory
> > + *              traffic to. These numbers should correspond with the port number
> > + *              in the PCIe Link Capabilities structure.
> > + *
> > + * Return: 0 if decoder was successfully added.
> > + *
> > + * Certain types of decoders may not have any targets. The main example of this
> > + * is an endpoint device. A more awkward example is a hostbridge whose root
> > + * ports get hot added (technically possible, though unlikely).
> > + */
> >  int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
> >                     int *target_map)
> >  {
> > --
> > 2.33.0
> >

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 03/13] cxl/core: Ignore interleave when adding decoders
  2021-09-10 19:00     ` Dan Williams
@ 2021-09-11 17:30       ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-11 17:30 UTC (permalink / raw)
  To: Dan Williams
  Cc: Jonathan Cameron, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On 21-09-10 12:00:29, Dan Williams wrote:
> On Fri, Sep 3, 2021 at 7:25 AM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Thu, 2 Sep 2021 12:50:07 -0700
> > Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > > Decoders will be added to the bus either already active (committed in
> > > spec parlance), or inactive. From the driver perspective, the set of
> > > devices comprising the former are those which are brought up by system
> > > firmware; decoders that implement: volatile regions, persistent regions,
> > > or platform specific (ie. CFMWS) constraints. Such devices have a given
> > > interleave programming already in place. Inactive decoders on the other
> > > hand, do not have any interleave programming in place. The set of
> > > devices comprising that are hostbridges, switches, and endpoint devices.
> > >
> > > Allow adding inactive decoders by removing this check.
> 
> I thought I agreed with this initially, but the spec initializes the
> default value of IW to 0 (== x1 interleave). It is impossible for a
> decoder to ever have less than one interleave-way defined. Instead
> "Decoder Size == 0" is a disabled decoder.

Well I later add state as to whether or not the decoder is active too. I don't
disagree with your logic, though I find this more awkward than what my patch
does. For the sake of moving things forward smoothly, I will drop this patch and
simply set iw = 1 for new, inactive decoders being enumerated.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 10/13] cxl/core: Map component registers for ports
  2021-09-10 23:52     ` Dan Williams
@ 2021-09-13  8:29       ` Jonathan Cameron
  0 siblings, 0 replies; 60+ messages in thread
From: Jonathan Cameron @ 2021-09-13  8:29 UTC (permalink / raw)
  To: Dan Williams
  Cc: Ben Widawsky, linux-cxl, Alison Schofield, Ira Weiny, Vishal Verma

On Fri, 10 Sep 2021 16:52:48 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> On Fri, Sep 3, 2021 at 9:14 AM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Thu, 2 Sep 2021 12:50:14 -0700
> > Ben Widawsky <ben.widawsky@intel.com> wrote:
> >  
> > > Component registers are implemented for CXL.mem/cache operations. The
> > > cxl_pci driver handles enumerating CXL devices with the CXL.io protocol.
> > > The driver for managing CXL.mem/cache operations will need the component
> > > registers mapped and the mapping cannot be shared across two devices.
> > >
> > > For now, it's fine to relinquish this mapping in cxl_pci. CXL IDE is one
> > > exception (perhaps others will exist) where it might be desirable to
> > > have the cxl_pci driver do negotiation. For this case, it probably will
> > > make sense to create an ephemeral mapping. Further looking, there might
> > > need to be a cxl_core mechanism to allow arbitrating access to the
> > > component registers.  
> >
> >  
> > >
> > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>  
> >
> > As you predicted I don't like this. Needs some thought on how to get
> > around the mapping games though and it's Friday afternoon so I'm not
> > going to offer any concrete answers...
> >
> > Not totally obvious to me where RAS will be handled as well.
> > I think we definitely need an arbitration mechanism here.  
> 
> Poison consumption is handled via typical memory_failure(). Event
> record and event interrupts can be handled by the PCI driver
> potentially notifying the memdev driver if necessary. Port specific
> error handling (RAS component registers) can be handled by the port
> driver.
> 
> > Wouldn't it have been nice if all these capabilities had been nicely
> > padded so we could map them individually.  Oh well!
> > Gut feeling is this will only get worse for future versions of the spec
> > so we should assume there will be lots of stuff shoved in here.  
> 
> I'd prefer to run away from arbitration and towards sub-drivers
> providing services with distinct lines of ownership. In the rare cases
> where a driver can not act independently there should be a well
> defined driver API to request help from the register block owner, not
> pass around access to the register block.

Agreed. Where possible keep ownership well defined + API to deal
with the occasional cross over point.

J

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-10 19:19   ` Dan Williams
@ 2021-09-13 16:11     ` Ben Widawsky
  2021-09-13 22:07       ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 16:11 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-10 12:19:24, Dan Williams wrote:
> On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > Endpoints have decoders too. It is useful to share the same
> > infrastructure from cxl_core. Endpoints do not have dports (downstream
> > targets), only the underlying physical medium. As a result, some special
> > casing is needed.
> >
> > There is no functional change introduced yet as endpoints don't actually
> > enumerate decoders yet.
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
> >  1 file changed, 25 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 8d5061b0794d..6202ce5a5ac2 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
> >         NULL,
> >  };
> >
> > +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> > +       &cxl_decoder_base_attribute_group,
> > +       &cxl_base_attribute_group,
> > +       NULL,
> > +};
> > +
> >  static void cxl_decoder_release(struct device *dev)
> >  {
> >         struct cxl_decoder *cxld = to_cxl_decoder(dev);
> > @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
> >         kfree(cxld);
> >  }
> >
> > +static const struct device_type cxl_decoder_endpoint_type = {
> > +       .name = "cxl_decoder_endpoint",
> > +       .release = cxl_decoder_release,
> > +       .groups = cxl_decoder_endpoint_attribute_groups,
> > +};
> > +
> >  static const struct device_type cxl_decoder_switch_type = {
> >         .name = "cxl_decoder_switch",
> >         .release = cxl_decoder_release,
> > @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
> >         .groups = cxl_decoder_root_attribute_groups,
> >  };
> >
> > +static bool is_endpoint_decoder(struct device *dev)
> > +{
> > +       return dev->type == &cxl_decoder_endpoint_type;
> > +}
> > +
> >  bool is_root_decoder(struct device *dev)
> >  {
> >         return dev->type == &cxl_decoder_root_type;
> > @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >         struct device *dev;
> >         int rc = 0;
> >
> > -       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> > +       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
> >                 return ERR_PTR(-EINVAL);
> >
> >         cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> > @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >         dev->parent = &port->dev;
> >         dev->bus = &cxl_bus_type;
> >
> > +       /* Endpoints don't have a target list */
> > +       if (nr_targets == 0)
> > +               dev->type = &cxl_decoder_endpoint_type;
> 
> Do you also plan to introduce the concept of endpoint ports, and if
> yes should that come before this patch? That would seem to be more
> robust than, for example, allowing a switch port to carry an endpoint
> decoder object as this allows.

I didn't see a need as of yet to differentiate between endpoint ports and other
ports. I don't entirely understand what you mean by "allowing a switch port to
carry an endpoint decoder" means. Can you please elaborate?

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-03 14:35   ` Jonathan Cameron
@ 2021-09-13 16:19     ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 16:19 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On 21-09-03 15:35:42, Jonathan Cameron wrote:
> On Thu, 2 Sep 2021 12:50:08 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
> 
> > Endpoints have decoders too. It is useful to share the same
> > infrastructure from cxl_core. Endpoints do not have dports (downstream
> > targets), only the underlying physical medium. As a result, some special
> > casing is needed.
> > 
> > There is no functional change introduced yet as endpoints don't actually
> > enumerate decoders yet.
> > 
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> 
> Comments inline...
> 
> > ---
> >  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
> >  1 file changed, 25 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 8d5061b0794d..6202ce5a5ac2 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
> >  	NULL,
> >  };
> >  
> > +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> > +	&cxl_decoder_base_attribute_group,
> > +	&cxl_base_attribute_group,
> > +	NULL,
> > +};
> > +
> >  static void cxl_decoder_release(struct device *dev)
> >  {
> >  	struct cxl_decoder *cxld = to_cxl_decoder(dev);
> > @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
> >  	kfree(cxld);
> >  }
> >  
> > +static const struct device_type cxl_decoder_endpoint_type = {
> > +	.name = "cxl_decoder_endpoint",
> > +	.release = cxl_decoder_release,
> > +	.groups = cxl_decoder_endpoint_attribute_groups,
> > +};
> > +
> >  static const struct device_type cxl_decoder_switch_type = {
> >  	.name = "cxl_decoder_switch",
> >  	.release = cxl_decoder_release,
> > @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
> >  	.groups = cxl_decoder_root_attribute_groups,
> >  };
> >  
> > +static bool is_endpoint_decoder(struct device *dev)
> > +{
> > +	return dev->type == &cxl_decoder_endpoint_type;
> > +}
> > +
> >  bool is_root_decoder(struct device *dev)
> >  {
> >  	return dev->type == &cxl_decoder_root_type;
> > @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >  	struct device *dev;
> >  	int rc = 0;
> >  
> > -	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> 
> Why do we let nr_targets go negative?  Could make the parameter unsigned perhaps or
> check for that here (even though it makes no sense).

Unsigned seems like a good idea to me as well.

> 
> > +	if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
> >  		return ERR_PTR(-EINVAL);
> >  
> >  	cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> > @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> >  	dev->parent = &port->dev;
> >  	dev->bus = &cxl_bus_type;
> >  
> > +	/* Endpoints don't have a target list */
> > +	if (nr_targets == 0)
> > +		dev->type = &cxl_decoder_endpoint_type;
> 
> Hmm. Whilst the check seems valid, it's a bit inelegant.
> The capability register unhelpfully simply has it defined
> as not applicable rather than 0, so I'd be nervous that might
> end up here (not checked yet).

AFAICT, 0 isn't a valid value in the register at all. Additionally, from the API
perspective I don't see much of an issue because nr_targets is the decoded
value. So the only entity that should call this with a value of 0 would have to
be something which is ignoring the field in the capability register, which would
only be an endpoint.

> 
> >  	/* root ports do not have a cxl_port_type parent */
> > -	if (port->dev.parent->type == &cxl_port_type)
> > +	else if (port->dev.parent->type == &cxl_port_type)
> >  		dev->type = &cxl_decoder_switch_type;
> >  	else
> >  		dev->type = &cxl_decoder_root_type;
> > @@ -532,9 +552,11 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
> >  	if (IS_ERR(cxld))
> >  		return PTR_ERR(cxld);
> >  
> > +	dev = &cxld->dev;
> > +
> >  	port = to_cxl_port(cxld->dev.parent);
> >  	device_lock(&port->dev);
> > -	if (list_empty(&port->dports)) {
> > +	if (is_endpoint_decoder(dev) && list_empty(&port->dports)) {
> >  		rc = -EINVAL;
> >  		goto out_unlock;
> >  	}
> > @@ -551,7 +573,6 @@ int cxl_decoder_add(struct device *host, struct cxl_decoder *cxld,
> >  	}
> >  	device_unlock(&port->dev);
> >  
> > -	dev = &cxld->dev;
> >  	rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
> >  	if (rc)
> >  		return rc;
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 06/13] cxl/mem: Introduce cxl_mem driver
  2021-09-10 21:32   ` Dan Williams
@ 2021-09-13 16:46     ` Ben Widawsky
  2021-09-13 19:37       ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 16:46 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-10 14:32:35, Dan Williams wrote:
> On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > CXL endpoints that participate in the CXL.mem protocol require extra
> > control to ensure architectural constraints are met for device
> > management. The most straight-forward way to achieve control of these
> > endpoints is with a new driver that can bind to such devices. This
> > driver will also be responsible for enumerating the switches that
> > connect the endpoint to the hostbridge.
> >
> > cxl_core already understands the concept of a memdev, but the core [by
> > design] does not comprehend all the topological constraints.
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  .../driver-api/cxl/memory-devices.rst         |  3 ++
> >  drivers/cxl/Makefile                          |  3 +-
> >  drivers/cxl/core/bus.c                        |  2 +
> >  drivers/cxl/core/core.h                       |  1 +
> >  drivers/cxl/core/memdev.c                     |  2 +-
> >  drivers/cxl/cxl.h                             |  1 +
> >  drivers/cxl/mem.c                             | 49 +++++++++++++++++++
> >  7 files changed, 59 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/cxl/mem.c
> >
> > diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> > index a18175bae7a6..00d141071570 100644
> > --- a/Documentation/driver-api/cxl/memory-devices.rst
> > +++ b/Documentation/driver-api/cxl/memory-devices.rst
> > @@ -28,6 +28,9 @@ CXL Memory Device
> >  .. kernel-doc:: drivers/cxl/pci.c
> >     :internal:
> >
> > +.. kernel-doc:: drivers/cxl/mem.c
> > +   :doc: cxl mem
> > +
> >  CXL Core
> >  --------
> >  .. kernel-doc:: drivers/cxl/cxl.h
> > diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
> > index d1aaabc940f3..d912ac4e3f0c 100644
> > --- a/drivers/cxl/Makefile
> > +++ b/drivers/cxl/Makefile
> > @@ -1,9 +1,10 @@
> >  # SPDX-License-Identifier: GPL-2.0
> >  obj-$(CONFIG_CXL_BUS) += core/
> > -obj-$(CONFIG_CXL_MEM) += cxl_pci.o
> > +obj-$(CONFIG_CXL_MEM) += cxl_mem.o cxl_pci.o
> 
> I'd rather now have a separate CONFIG_CXL_PCI Kconfig symbol for
> cxl_pci and let CONFIG_CXL_MEM just mean cxl_mem. I.e. maybe there
> will be a non-cxl_pci agent that registers cxl_memdev objects, or
> maybe someone will only want manageability and not CXL.mem operation
> enabled in their kernel.
> 

I would have given an argument you often use and suggest we wait until that
usage exists since later patches require both exist so that the pci driver can
give the mem driver the component register location. However, reading ahead it
sounds like that's going to have to get rewritten.

> 
> >  obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
> >  obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
> >
> > +cxl_mem-y := mem.o
> >  cxl_pci-y := pci.o
> >  cxl_acpi-y := acpi.o
> >  cxl_pmem-y := pmem.o
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 6202ce5a5ac2..256e55dc2a3b 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -641,6 +641,8 @@ static int cxl_device_id(struct device *dev)
> >                 return CXL_DEVICE_NVDIMM_BRIDGE;
> >         if (dev->type == &cxl_nvdimm_type)
> >                 return CXL_DEVICE_NVDIMM;
> > +       if (dev->type == &cxl_memdev_type)
> > +               return CXL_DEVICE_ENDPOINT;
> 
> Perhaps CXL_DEVICE_MEMORY_{INTERFACE,EXPANDER}? "ENDPOINT" seems too generic.
> 
> >         return 0;
> >  }
> >
> > diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> > index e0c9aacc4e9c..dea246cb7c58 100644
> > --- a/drivers/cxl/core/core.h
> > +++ b/drivers/cxl/core/core.h
> > @@ -6,6 +6,7 @@
> >
> >  extern const struct device_type cxl_nvdimm_bridge_type;
> >  extern const struct device_type cxl_nvdimm_type;
> > +extern const struct device_type cxl_memdev_type;
> >
> >  extern struct attribute_group cxl_base_attribute_group;
> >
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index ee61202c7aab..c9dd054bd813 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -127,7 +127,7 @@ static const struct attribute_group *cxl_memdev_attribute_groups[] = {
> >         NULL,
> >  };
> >
> > -static const struct device_type cxl_memdev_type = {
> > +const struct device_type cxl_memdev_type = {
> >         .name = "cxl_memdev",
> >         .release = cxl_memdev_release,
> >         .devnode = cxl_memdev_devnode,
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index 708bfe92b596..b48bdbefd949 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -315,6 +315,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
> >
> >  #define CXL_DEVICE_NVDIMM_BRIDGE       1
> >  #define CXL_DEVICE_NVDIMM              2
> > +#define CXL_DEVICE_ENDPOINT            3
> >
> >  #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
> >  #define CXL_MODALIAS_FMT "cxl:t%d"
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > new file mode 100644
> > index 000000000000..978a54b0a51a
> > --- /dev/null
> > +++ b/drivers/cxl/mem.c
> > @@ -0,0 +1,49 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> > +#include <linux/device.h>
> > +#include <linux/module.h>
> > +
> > +#include "cxlmem.h"
> > +
> > +/**
> > + * DOC: cxl mem
> > + *
> > + * CXL memory endpoint devices and switches are CXL capable devices that are
> > + * participating in CXL.mem protocol. Their functionality builds on top of the
> > + * CXL.io protocol that allows enumerating and configuring components via
> > + * standard PCI mechanisms.
> > + *
> > + * The cxl_mem driver implements enumeration and control over these CXL
> > + * components.
> > + */
> > +
> > +static int cxl_mem_probe(struct device *dev)
> > +{
> > +       return -EOPNOTSUPP;
> 
> Why not just merge this patch with the one that fills these in? I'm
> otherwise not understanding the value of having this stopping point in
> someone's future bisect run. Even commit 4cdadfd5e0a7 ("cxl/mem:
> Introduce a driver for CXL-2.0-Type-3 endpoints") had a functional
> probe.
> 

Okay. It serves no purpose for bisection. It was primarily to introduce the
drivers kdocs and hookup in core. I don't think this is a new thing for our CXL
patches, but I admit I don't pay close attention to these things.

> > +}
> > +
> > +static void cxl_mem_remove(struct device *dev)
> > +{
> > +}
> > +
> > +static struct cxl_driver cxl_mem_driver = {
> > +       .name = "cxl_mem",
> > +       .probe = cxl_mem_probe,
> > +       .remove = cxl_mem_remove,
> 
> Empty ->remove() callbacks don't need to be registered.
> 
> > +       .id = CXL_DEVICE_ENDPOINT,
> > +};
> > +
> > +static __init int cxl_mem_init(void)
> > +{
> > +       return cxl_driver_register(&cxl_mem_driver);
> > +}
> > +
> > +static __exit void cxl_mem_exit(void)
> > +{
> > +       cxl_driver_unregister(&cxl_mem_driver);
> > +}
> > +
> > +MODULE_LICENSE("GPL v2");
> > +module_init(cxl_mem_init);
> > +module_exit(cxl_mem_exit);
> 
> Perhaps, before this adds more boilerplate, go define a:
> 
> #define module_cxl_driver(__cxl_driver) \
>         module_driver(__cxl_driver, cxl_driver_register, cxl_driver_unregister)
> 
> ...as a lead-in patch?

Okay.


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-03 15:21   ` Jonathan Cameron
@ 2021-09-13 19:01     ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 19:01 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny,
	Vishal Verma, Bjorn Helgaas, linux-pci

On 21-09-03 16:21:57, Jonathan Cameron wrote:
> On Thu, 2 Sep 2021 12:50:11 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
> 
> > If the "upstream" port of the endpoint is an enumerated downstream CXL
> > port, and the device itself is CXL capable and enabled, the memdev
> > driver can bind. This binding useful for region configuration/creation
> > because it provides a clean way for the region code to determine if the
> > memdev is actually CXL capable.
> > 
> > A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> > the end of ACPI probing (see comment in code for details). Switch
> > enumeration will be done as a follow-on patch. As a result, if a switch
> > is in the topology the memdev driver will not bind to any devices.
> > 
> > CXL.mem capability is checked lazily at the time a region is bound.
> > This is in line with the other configuration parameters.
> > 
> > Below is an example (mem0, and mem1) of CXL memdev devices that now
> > exist on the bus.
> > 
> > /sys/bus/cxl/devices/
> > ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> > ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> > ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
> > ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> > ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> > ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> > └── root0 -> ../../../devices/platform/ACPI0017:00/root0
> > 
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> 
> +CC Bjorn.  Given we are moving the (nearly) generic DVSEC lookup routine, perhaps now
> is time to move it into PCI core code?
> 

Fine with me. Adding linux-pci as well...

> > ---
> >  drivers/cxl/acpi.c        | 27 +++++++-----------
> >  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/core/memdev.c |  6 ++++
> >  drivers/cxl/cxl.h         |  2 ++
> >  drivers/cxl/cxlmem.h      |  2 ++
> >  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
> >  drivers/cxl/pci.c         | 23 ---------------
> >  drivers/cxl/pci.h         |  7 ++++-
> >  8 files changed, 141 insertions(+), 41 deletions(-)
> > 
> 
> ...
> 
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 256e55dc2a3b..56f57302d27b 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> 
> ...
> 
> > @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
> >  }
> >  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
> >  
> > +/**
> > + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> > + * @pdev: PCI device to search for the DVSEC
> > + * @dvsec: DVSEC id to look for
> > + *
> > + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> > + */
> > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> > +{
> > +	int pos;
> > +
> > +	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> > +	if (!pos)
> > +		return 0;
> > +
> > +	while (pos) {
> > +		u16 vendor, id;
> > +
> > +		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> > +		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> > +		if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> > +			return pos;
> > +
> > +		pos = pci_find_next_ext_capability(pdev, pos,
> > +						   PCI_EXT_CAP_ID_DVSEC);
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);
> 
> That's not going to work. (pci/mem)  + Can we move this to the PCI core now?
> Wasn't done originally because there were several copies in various different
> trees that needed to all come together. Oddly only this one seems to have
> made it in though.

I'm confused about why it won't work - nevertheless, PCI core is probably the
right place.

Something like?

/**
 * pci_find_dvsec_capability - Find DVSEC for vendor
 * @dev: PCI device to query
 * @vendor: Vendor ID to match for the DVSEC
 * @dvsec: Designated Vendor-specific capability ID
 *
 * If DVSEC has Vendor ID @vendor and DVSEC ID @dvsec return the capability
 * offset in config space; otherwise return 0.
 */
u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec)
{
	int pos;

	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DVSEC);
	if (!pos)
		return 0;

	while (pos) {
		u16 vendor, id;

		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
		pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
		if (vendor == vendor && dvsec == id)
			return pos;

		pos = pci_find_next_ext_capability(dev, pos, PCI_EXT_CAP_ID_DVSEC);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(pci_find_dvsec_capability);

> 
> 
> > +
> >  /**
> >   * __cxl_driver_register - register a driver for the cxl bus
> >   * @cxl_drv: cxl driver structure to attach
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index c9dd054bd813..0068b5ff5f3e 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
> >  {
> >  	unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
> >  }
> > +
> > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> > +{
> 
> This feels like it needs a comment to say why that's a valid check to use
> to find out if it is mem capable.
> 
> > +	return !!cxlmd->dev.driver;
> > +}
> > +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index b48bdbefd949..a168520d741b 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> >  				   resource_size_t component_reg_phys,
> >  				   struct cxl_port *parent_port);
> >  
> > +bool is_cxl_port(struct device *dev);
> >  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
> >  		  resource_size_t component_reg_phys);
> > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
> >  
> >  struct cxl_decoder *to_cxl_decoder(struct device *dev);
> >  bool is_root_decoder(struct device *dev);
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 811b24451604..88264204c4b9 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> >  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> >  				       struct cxl_mem *cxlm);
> >  
> > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> > +
> >  /**
> >   * struct cxl_mbox_cmd - A command to be submitted to hardware.
> >   * @opcode: (input) The command set and command submitted to hardware.
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index 978a54b0a51a..b6dc34d18a86 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -2,8 +2,10 @@
> >  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> >  #include <linux/device.h>
> >  #include <linux/module.h>
> > +#include <linux/pci.h>
> >  
> >  #include "cxlmem.h"
> > +#include "pci.h"
> >  
> >  /**
> >   * DOC: cxl mem
> > @@ -17,9 +19,60 @@
> >   * components.
> >   */
> >  
> > +static int port_match(struct device *dev, const void *data)
> > +{
> > +	struct cxl_port *port;
> > +
> > +	if (!is_cxl_port(dev))
> > +		return 0;
> > +
> > +	port = to_cxl_port(dev);
> > +
> > +	if (find_dport_by_dev(port, (struct device *)data))
> Why the cast?
> 
> If this isn't modified in later patches
> 
> 	return find_dport_by_dev(port, data);
> 
> 
> > +		return 1;
> > +
> > +	return 0;
> > +}
> > +
> > +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> > +{
> > +	int pcie_dvsec;
> > +	u16 dvsec_ctrl;
> > +
> > +	pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> > +	if (!pcie_dvsec) {
> > +		dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> > +		return false;
> > +	}
> > +
> > +	pci_read_config_word(pdev,
> > +			     pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> > +			     &dvsec_ctrl);
> > +	if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> > +		dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
> 
> In the ctrl field that indicates it's not on, rather than not supported.
> Hence I think the dev_info message is wrong.
> 
> > +		return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> >  static int cxl_mem_probe(struct device *dev)
> >  {
> > -	return -EOPNOTSUPP;
> > +	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> > +	struct cxl_mem *cxlm = cxlmd->cxlm;
> > +	struct device *pdev_parent = cxlm->dev->parent;
> > +	struct pci_dev *pdev = to_pci_dev(cxlm->dev);
> > +	struct device *port_dev;
> > +
> > +	if (!is_cxl_mem_enabled(pdev))
> > +		return -ENODEV;
> > +
> > +	/* TODO: if parent is a switch, this will fail. */
> > +	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
> > +	if (!port_dev)
> > +		return -ENODEV;
> > +
> > +	return 0;
> >  }
> >  
> >  static void cxl_mem_remove(struct device *dev)
> 
> ...
> 

I took all the rest of the feedback up to here...

> > diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
> > index 8c1a58813816..d6b9978d05b0 100644
> > --- a/drivers/cxl/pci.h
> > +++ b/drivers/cxl/pci.h
> > @@ -11,7 +11,10 @@
> >   */
> >  #define PCI_DVSEC_HEADER1_LENGTH_MASK	GENMASK(31, 20)
> >  #define PCI_DVSEC_VENDOR_ID_CXL		0x1E98
> > -#define PCI_DVSEC_ID_CXL		0x0
> > +
> > +#define PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID	0x0
> 
> Why the rename to this?  DVSEC x3???  Can we get away with something like...
> 
> > +#define PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET	0xC
> > +#define   CXL_PCIE_MEM_ENABLE			BIT(2)
> >  
> >  #define PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID	0x8
> 
> Mind you I'm not clear why this one has DVSEC twice either...
> 
> >  #define PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET	0xC
> 
> This one has nothing to do with DVSEC ID... 
> 

I've already forgotten the motivation for the existing names and I don't care to
go back in the history. I was trying to create a schema here.

As you point out, both the description name and the field name introduce
redundant words. So how about all redundant of CXL and DVSEC are removed and the
schema becomes:

CXL DVSEC IDs : CXL_DVSEC_<description>
Offsets: DVSEC_<description>_<field>_OFFSET
Bits/mask DVSEC_<description>_<field>_<attribute>

#define CXL_DVSEC_PCIE_DEVICE		0
#define CXL_DVSEC_FUNCTION_MAP		2
#define CXL_DVSEC_PORT_EXTENSIONS	3
#define CXL_DVSEC_PORT_GPF		4
#define CXL_DVSEC_DEVICE_GPF		5
#define CXL_DVSEC_PCIE_FLEXBUS_PORT	7
#define CXL_DVSEC_REGISTER_LOCATOR	8
#define CXL_DVSEC_MLD			9
#define CXL_DVSEC_PCIE_TEST_CAPABILITY	10

Then an offset within one of the DVSECs...
#define CXL_DVSEC_REGISTER_LOCATOR		8
#define   DVSEC_REGISTER_LOCATOR_BLOCK1_OFFSET	0xC

Then a bit/mask...
#define CXL_DVSEC_PCIE_DEVICE			0
#define  DVSEC_PCIE_DEVICE_CAP_OFFSET		0xA
#define    DVSEC_PCIE_DEVICE_CAP_MEM_CAPABLE	BIT(2)

I'm open to other suggestions. I feel like translating registers from spec to
header files is one thing I've done over and over on many projects and I think
I'm unhappy with every solution we've managed to implement.

> > @@ -29,4 +32,6 @@
> >  
> >  #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
> >  
> > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
> > +
> >  #endif /* __CXL_PCI_H__ */
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 06/13] cxl/mem: Introduce cxl_mem driver
  2021-09-13 16:46     ` Ben Widawsky
@ 2021-09-13 19:37       ` Dan Williams
  0 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-13 19:37 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Mon, Sep 13, 2021 at 9:47 AM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> On 21-09-10 14:32:35, Dan Williams wrote:
> > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > >
> > > CXL endpoints that participate in the CXL.mem protocol require extra
> > > control to ensure architectural constraints are met for device
> > > management. The most straight-forward way to achieve control of these
> > > endpoints is with a new driver that can bind to such devices. This
> > > driver will also be responsible for enumerating the switches that
> > > connect the endpoint to the hostbridge.
> > >
> > > cxl_core already understands the concept of a memdev, but the core [by
> > > design] does not comprehend all the topological constraints.
> > >
> > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > ---
> > >  .../driver-api/cxl/memory-devices.rst         |  3 ++
> > >  drivers/cxl/Makefile                          |  3 +-
> > >  drivers/cxl/core/bus.c                        |  2 +
> > >  drivers/cxl/core/core.h                       |  1 +
> > >  drivers/cxl/core/memdev.c                     |  2 +-
> > >  drivers/cxl/cxl.h                             |  1 +
> > >  drivers/cxl/mem.c                             | 49 +++++++++++++++++++
> > >  7 files changed, 59 insertions(+), 2 deletions(-)
> > >  create mode 100644 drivers/cxl/mem.c
> > >
> > > diff --git a/Documentation/driver-api/cxl/memory-devices.rst b/Documentation/driver-api/cxl/memory-devices.rst
> > > index a18175bae7a6..00d141071570 100644
> > > --- a/Documentation/driver-api/cxl/memory-devices.rst
> > > +++ b/Documentation/driver-api/cxl/memory-devices.rst
> > > @@ -28,6 +28,9 @@ CXL Memory Device
> > >  .. kernel-doc:: drivers/cxl/pci.c
> > >     :internal:
> > >
> > > +.. kernel-doc:: drivers/cxl/mem.c
> > > +   :doc: cxl mem
> > > +
> > >  CXL Core
> > >  --------
> > >  .. kernel-doc:: drivers/cxl/cxl.h
> > > diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
> > > index d1aaabc940f3..d912ac4e3f0c 100644
> > > --- a/drivers/cxl/Makefile
> > > +++ b/drivers/cxl/Makefile
> > > @@ -1,9 +1,10 @@
> > >  # SPDX-License-Identifier: GPL-2.0
> > >  obj-$(CONFIG_CXL_BUS) += core/
> > > -obj-$(CONFIG_CXL_MEM) += cxl_pci.o
> > > +obj-$(CONFIG_CXL_MEM) += cxl_mem.o cxl_pci.o
> >
> > I'd rather now have a separate CONFIG_CXL_PCI Kconfig symbol for
> > cxl_pci and let CONFIG_CXL_MEM just mean cxl_mem. I.e. maybe there
> > will be a non-cxl_pci agent that registers cxl_memdev objects, or
> > maybe someone will only want manageability and not CXL.mem operation
> > enabled in their kernel.
> >
>
> I would have given an argument you often use and suggest we wait until that
> usage exists since later patches require both exist so that the pci driver can
> give the mem driver the component register location. However, reading ahead it
> sounds like that's going to have to get rewritten.

The PCI driver gives the component information to the device, not the
driver, so the driver need not be enabled to consume the component
registers.

Another alternative if you want to avoid the complexity of another
module in the near term is to move the driver into the core. I.e. make
the core a multi-driver module. The libnvdimm core uses that scheme so
it can make assumptions like "driver guaranteed to be attached after
return from device_add()" as it eliminates the asynchronous module
lookup and loading. I can see that being a useful property for the
port driver, but I think the memdev driver is ok to be either
integrated, or independent. I just don't think it belongs to the same
config as cxl_pci.

>
> >
> > >  obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
> > >  obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
> > >
> > > +cxl_mem-y := mem.o
> > >  cxl_pci-y := pci.o
> > >  cxl_acpi-y := acpi.o
> > >  cxl_pmem-y := pmem.o
> > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > index 6202ce5a5ac2..256e55dc2a3b 100644
> > > --- a/drivers/cxl/core/bus.c
> > > +++ b/drivers/cxl/core/bus.c
> > > @@ -641,6 +641,8 @@ static int cxl_device_id(struct device *dev)
> > >                 return CXL_DEVICE_NVDIMM_BRIDGE;
> > >         if (dev->type == &cxl_nvdimm_type)
> > >                 return CXL_DEVICE_NVDIMM;
> > > +       if (dev->type == &cxl_memdev_type)
> > > +               return CXL_DEVICE_ENDPOINT;
> >
> > Perhaps CXL_DEVICE_MEMORY_{INTERFACE,EXPANDER}? "ENDPOINT" seems too generic.
> >
> > >         return 0;
> > >  }
> > >
> > > diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> > > index e0c9aacc4e9c..dea246cb7c58 100644
> > > --- a/drivers/cxl/core/core.h
> > > +++ b/drivers/cxl/core/core.h
> > > @@ -6,6 +6,7 @@
> > >
> > >  extern const struct device_type cxl_nvdimm_bridge_type;
> > >  extern const struct device_type cxl_nvdimm_type;
> > > +extern const struct device_type cxl_memdev_type;
> > >
> > >  extern struct attribute_group cxl_base_attribute_group;
> > >
> > > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > > index ee61202c7aab..c9dd054bd813 100644
> > > --- a/drivers/cxl/core/memdev.c
> > > +++ b/drivers/cxl/core/memdev.c
> > > @@ -127,7 +127,7 @@ static const struct attribute_group *cxl_memdev_attribute_groups[] = {
> > >         NULL,
> > >  };
> > >
> > > -static const struct device_type cxl_memdev_type = {
> > > +const struct device_type cxl_memdev_type = {
> > >         .name = "cxl_memdev",
> > >         .release = cxl_memdev_release,
> > >         .devnode = cxl_memdev_devnode,
> > > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > > index 708bfe92b596..b48bdbefd949 100644
> > > --- a/drivers/cxl/cxl.h
> > > +++ b/drivers/cxl/cxl.h
> > > @@ -315,6 +315,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
> > >
> > >  #define CXL_DEVICE_NVDIMM_BRIDGE       1
> > >  #define CXL_DEVICE_NVDIMM              2
> > > +#define CXL_DEVICE_ENDPOINT            3
> > >
> > >  #define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
> > >  #define CXL_MODALIAS_FMT "cxl:t%d"
> > > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > > new file mode 100644
> > > index 000000000000..978a54b0a51a
> > > --- /dev/null
> > > +++ b/drivers/cxl/mem.c
> > > @@ -0,0 +1,49 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> > > +#include <linux/device.h>
> > > +#include <linux/module.h>
> > > +
> > > +#include "cxlmem.h"
> > > +
> > > +/**
> > > + * DOC: cxl mem
> > > + *
> > > + * CXL memory endpoint devices and switches are CXL capable devices that are
> > > + * participating in CXL.mem protocol. Their functionality builds on top of the
> > > + * CXL.io protocol that allows enumerating and configuring components via
> > > + * standard PCI mechanisms.
> > > + *
> > > + * The cxl_mem driver implements enumeration and control over these CXL
> > > + * components.
> > > + */
> > > +
> > > +static int cxl_mem_probe(struct device *dev)
> > > +{
> > > +       return -EOPNOTSUPP;
> >
> > Why not just merge this patch with the one that fills these in? I'm
> > otherwise not understanding the value of having this stopping point in
> > someone's future bisect run. Even commit 4cdadfd5e0a7 ("cxl/mem:
> > Introduce a driver for CXL-2.0-Type-3 endpoints") had a functional
> > probe.
> >
>
> Okay. It serves no purpose for bisection. It was primarily to introduce the
> drivers kdocs and hookup in core. I don't think this is a new thing for our CXL
> patches, but I admit I don't pay close attention to these things.

It's not a major concern, but it's just an extra error message that
won't pop up in someone's bisect run.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-13 16:11     ` Ben Widawsky
@ 2021-09-13 22:07       ` Dan Williams
  2021-09-13 23:19         ` Ben Widawsky
  0 siblings, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-13 22:07 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Mon, Sep 13, 2021 at 9:11 AM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> On 21-09-10 12:19:24, Dan Williams wrote:
> > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > >
> > > Endpoints have decoders too. It is useful to share the same
> > > infrastructure from cxl_core. Endpoints do not have dports (downstream
> > > targets), only the underlying physical medium. As a result, some special
> > > casing is needed.
> > >
> > > There is no functional change introduced yet as endpoints don't actually
> > > enumerate decoders yet.
> > >
> > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > ---
> > >  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
> > >  1 file changed, 25 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > index 8d5061b0794d..6202ce5a5ac2 100644
> > > --- a/drivers/cxl/core/bus.c
> > > +++ b/drivers/cxl/core/bus.c
> > > @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
> > >         NULL,
> > >  };
> > >
> > > +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> > > +       &cxl_decoder_base_attribute_group,
> > > +       &cxl_base_attribute_group,
> > > +       NULL,
> > > +};
> > > +
> > >  static void cxl_decoder_release(struct device *dev)
> > >  {
> > >         struct cxl_decoder *cxld = to_cxl_decoder(dev);
> > > @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
> > >         kfree(cxld);
> > >  }
> > >
> > > +static const struct device_type cxl_decoder_endpoint_type = {
> > > +       .name = "cxl_decoder_endpoint",
> > > +       .release = cxl_decoder_release,
> > > +       .groups = cxl_decoder_endpoint_attribute_groups,
> > > +};
> > > +
> > >  static const struct device_type cxl_decoder_switch_type = {
> > >         .name = "cxl_decoder_switch",
> > >         .release = cxl_decoder_release,
> > > @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
> > >         .groups = cxl_decoder_root_attribute_groups,
> > >  };
> > >
> > > +static bool is_endpoint_decoder(struct device *dev)
> > > +{
> > > +       return dev->type == &cxl_decoder_endpoint_type;
> > > +}
> > > +
> > >  bool is_root_decoder(struct device *dev)
> > >  {
> > >         return dev->type == &cxl_decoder_root_type;
> > > @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > >         struct device *dev;
> > >         int rc = 0;
> > >
> > > -       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> > > +       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
> > >                 return ERR_PTR(-EINVAL);
> > >
> > >         cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> > > @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > >         dev->parent = &port->dev;
> > >         dev->bus = &cxl_bus_type;
> > >
> > > +       /* Endpoints don't have a target list */
> > > +       if (nr_targets == 0)
> > > +               dev->type = &cxl_decoder_endpoint_type;
> >
> > Do you also plan to introduce the concept of endpoint ports, and if
> > yes should that come before this patch? That would seem to be more
> > robust than, for example, allowing a switch port to carry an endpoint
> > decoder object as this allows.
>
> I didn't see a need as of yet to differentiate between endpoint ports and other
> ports. I don't entirely understand what you mean by "allowing a switch port to
> carry an endpoint decoder" means. Can you please elaborate?

If endpoint ports were an explicit type then this check could make
sure that someone did not pass nr_targets set to 0 where the @port
argument is referring to a switch where the target_list must be
specified.

Either that, or a comment in kernel-doc for this routine about the
special meaning of nr_targets == 0 and expected usage.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-10 21:59   ` Dan Williams
@ 2021-09-13 22:10     ` Ben Widawsky
  2021-09-14 22:42       ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 22:10 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-10 14:59:29, Dan Williams wrote:
> On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > If the "upstream" port of the endpoint is an enumerated downstream CXL
> > port, and the device itself is CXL capable and enabled, the memdev
> > driver can bind. This binding useful for region configuration/creation
> > because it provides a clean way for the region code to determine if the
> > memdev is actually CXL capable.
> >
> > A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> > the end of ACPI probing (see comment in code for details). Switch
> > enumeration will be done as a follow-on patch. As a result, if a switch
> > is in the topology the memdev driver will not bind to any devices.
> >
> > CXL.mem capability is checked lazily at the time a region is bound.
> > This is in line with the other configuration parameters.
> >
> > Below is an example (mem0, and mem1) of CXL memdev devices that now
> > exist on the bus.
> >
> > /sys/bus/cxl/devices/
> > ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> > ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> > ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
> 
> I'm confused, this isn't showing anything new that did not already
> exist before this patch? What I think would be a useful shortcut is
> for memX devices to have an attribute that links back to their cxl
> root port after validation completes. Like an attribute group that
> arrives and disappears when the driver successfully binds and unbinds
> respectively.
> 

This was a copy-paste mistake. I meant to show the memX devices under cxl_mem
drivers, like this:
# tree /sys/bus/cxl/drivers/
/sys/bus/cxl/drivers/
├── cxl_mem
│   ├── bind
│   ├── mem0 -> ../../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem0
│   ├── mem1 -> ../../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem1
│   ├── uevent
│   └── unbind
├── cxl_nvdimm
│   ├── bind
│   ├── uevent
│   └── unbind
└── cxl_nvdimm_bridge
    ├── bind
    ├── uevent
    └── unbind


While I'm not opposed to add a link as you mention, I don't yet see the utility.
Are you thinking as primarily a convenience for userspace tooling? Is this
useful if you have a switch in the path?

> > ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> > ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> > ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> > └── root0 -> ../../../devices/platform/ACPI0017:00/root0
> >
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/acpi.c        | 27 +++++++-----------
> >  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
> >  drivers/cxl/core/memdev.c |  6 ++++
> >  drivers/cxl/cxl.h         |  2 ++
> >  drivers/cxl/cxlmem.h      |  2 ++
> >  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
> >  drivers/cxl/pci.c         | 23 ---------------
> >  drivers/cxl/pci.h         |  7 ++++-
> >  8 files changed, 141 insertions(+), 41 deletions(-)
> >
> > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > index 7130beffc929..fd14094bdb3f 100644
> > --- a/drivers/cxl/acpi.c
> > +++ b/drivers/cxl/acpi.c
> > @@ -240,21 +240,6 @@ __mock 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;
> > -}
> > -
> >  __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> >                                               struct device *dev)
> >  {
> > @@ -459,9 +444,19 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> >         if (rc)
> >                 goto out;
> >
> > -       if (IS_ENABLED(CONFIG_CXL_PMEM))
> > +       if (IS_ENABLED(CONFIG_CXL_PMEM)) {
> >                 rc = device_for_each_child(&root_port->dev, root_port,
> >                                            add_root_nvdimm_bridge);
> > +               if (rc)
> > +                       goto out;
> > +       }
> > +
> > +       /*
> > +        * While ACPI is scanning hostbridge ports, switches and memory devices
> > +        * may have been probed. Those devices will need to know whether the
> > +        * hostbridge is CXL capable.
> > +        */
> > +       rc = bus_rescan_devices(&cxl_bus_type);
> 
> I don't think it's a good idea to call bus_rescan_devices() from
> probe() context. This now sets up a lockdep dependency between the
> ACPI0017 device-lock and all the device-locks for every device on the
> cxl-bus. This is why the nvdimm code punts the rescan outside the lock
> to a workqueue.
> 
> Lockdep unfortunately won't complain about device-lock entanglements.
> One item for the backlog is to add device-lock validation to the cxl
> subsystem ala commit 87a30e1f05d7 ("driver-core, libnvdimm: Let device
> subsystems add local lockdep coverage")

Good catch. I will fix. This is now the second time I tripped over that backlog
item :-)

> 
> 
> >
> >  out:
> >         acpi_put_table(acpi_cedt);
> > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > index 256e55dc2a3b..56f57302d27b 100644
> > --- a/drivers/cxl/core/bus.c
> > +++ b/drivers/cxl/core/bus.c
> > @@ -8,6 +8,7 @@
> >  #include <linux/idr.h>
> >  #include <cxlmem.h>
> >  #include <cxl.h>
> > +#include <pci.h>
> >  #include "core.h"
> >
> >  /**
> > @@ -259,6 +260,12 @@ static const struct device_type cxl_port_type = {
> >         .groups = cxl_port_attribute_groups,
> >  };
> >
> > +bool is_cxl_port(struct device *dev)
> > +{
> > +       return dev->type == &cxl_port_type;
> > +}
> > +EXPORT_SYMBOL_GPL(is_cxl_port);
> > +
> >  struct cxl_port *to_cxl_port(struct device *dev)
> >  {
> >         if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
> > @@ -266,6 +273,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
> >                 return NULL;
> >         return container_of(dev, struct cxl_port, dev);
> >  }
> > +EXPORT_SYMBOL_GPL(to_cxl_port);
> >
> >  static void unregister_port(void *_port)
> >  {
> > @@ -424,6 +432,27 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
> >         return dup ? -EEXIST : 0;
> >  }
> >
> > +/**
> > + * find_dport_by_dev - gets downstream CXL port from a struct device
> > + * @port: cxl [upstream] port that "owns" the downstream port is being queried
> > + * @dev: The device that is backing the downstream port
> > + */
> > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const 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;
> > +}
> > +EXPORT_SYMBOL_GPL(find_dport_by_dev);
> 
> This wants to move into the "cxl_" prefix symbol namespace if it's now
> going to be a public function.
> 
> > +
> >  /**
> >   * cxl_add_dport - append downstream port data to a cxl_port
> >   * @port: the cxl_port that references this dport
> > @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
> >  }
> >  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
> >
> > +/**
> > + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> > + * @pdev: PCI device to search for the DVSEC
> > + * @dvsec: DVSEC id to look for
> > + *
> > + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> > + */
> > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> > +{
> > +       int pos;
> > +
> > +       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> > +       if (!pos)
> > +               return 0;
> > +
> > +       while (pos) {
> > +               u16 vendor, id;
> > +
> > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> > +               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> > +                       return pos;
> > +
> > +               pos = pci_find_next_ext_capability(pdev, pos,
> > +                                                  PCI_EXT_CAP_ID_DVSEC);
> > +       }
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);
> 
> Why not keep this enumeration in cxl_pci and have it record the
> component register block base address at cxl_memdev creation time?
> This would make it similar to cxl_port creation that takes a
> component_register base address argument.
> 

It does that, this is needed in addition to that to find certain DVSEC registers
that are used to determine CXL properties.

> > +
> >  /**
> >   * __cxl_driver_register - register a driver for the cxl bus
> >   * @cxl_drv: cxl driver structure to attach
> > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > index c9dd054bd813..0068b5ff5f3e 100644
> > --- a/drivers/cxl/core/memdev.c
> > +++ b/drivers/cxl/core/memdev.c
> > @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
> >  {
> >         unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
> >  }
> > +
> > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> > +{
> > +       return !!cxlmd->dev.driver;
> > +}
> > +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
> 
> Perhaps:
> 
> s/capable/{enabled,routed}/
> 
> The device is always capable, it's the hierarchy that will let it down.

I can rename to routed. From my perspective, capable and enabled aren't
synonymous. A capable device may not be enabled.

> 
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index b48bdbefd949..a168520d741b 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> >                                    resource_size_t component_reg_phys,
> >                                    struct cxl_port *parent_port);
> >
> > +bool is_cxl_port(struct device *dev);
> >  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
> >                   resource_size_t component_reg_phys);
> > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
> >
> >  struct cxl_decoder *to_cxl_decoder(struct device *dev);
> >  bool is_root_decoder(struct device *dev);
> > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > index 811b24451604..88264204c4b9 100644
> > --- a/drivers/cxl/cxlmem.h
> > +++ b/drivers/cxl/cxlmem.h
> > @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> >  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> >                                        struct cxl_mem *cxlm);
> >
> > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> > +
> >  /**
> >   * struct cxl_mbox_cmd - A command to be submitted to hardware.
> >   * @opcode: (input) The command set and command submitted to hardware.
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index 978a54b0a51a..b6dc34d18a86 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -2,8 +2,10 @@
> >  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> >  #include <linux/device.h>
> >  #include <linux/module.h>
> > +#include <linux/pci.h>
> >
> >  #include "cxlmem.h"
> > +#include "pci.h"
> >
> >  /**
> >   * DOC: cxl mem
> > @@ -17,9 +19,60 @@
> >   * components.
> >   */
> >
> > +static int port_match(struct device *dev, const void *data)
> > +{
> > +       struct cxl_port *port;
> > +
> > +       if (!is_cxl_port(dev))
> > +               return 0;
> > +
> > +       port = to_cxl_port(dev);
> > +
> > +       if (find_dport_by_dev(port, (struct device *)data))
> > +               return 1;
> > +
> > +       return 0;
> > +}
> > +
> > +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> > +{
> > +       int pcie_dvsec;
> > +       u16 dvsec_ctrl;
> > +
> > +       pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> > +       if (!pcie_dvsec) {
> > +               dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> > +               return false;
> > +       }
> > +
> > +       pci_read_config_word(pdev,
> > +                            pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> > +                            &dvsec_ctrl);
> > +       if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> > +               dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
> > +               return false;
> > +       }
> > +
> > +       return true;
> > +}
> > +
> >  static int cxl_mem_probe(struct device *dev)
> >  {
> > -       return -EOPNOTSUPP;
> > +       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> > +       struct cxl_mem *cxlm = cxlmd->cxlm;
> > +       struct device *pdev_parent = cxlm->dev->parent;
> > +       struct pci_dev *pdev = to_pci_dev(cxlm->dev);
> 
> It's not safe to assume that the parent of a cxlmd is a pci device.
> 

What can it be then? Isn't the parent always going to be a downstream port,
root port, or emulated port from cxl_test?

> > +       struct device *port_dev;
> > +
> > +       if (!is_cxl_mem_enabled(pdev))
> > +               return -ENODEV;
> 
> This isn't sufficient, this needs to walk the entire hierarchy, right?
> 

It was saved to a later patch because I have no way to actually test deeper
hierarchies at the moment. After I had already sent this, you and I discussed
not doing that. I can merge it together if there's no perceived value in keeping
them separate, which it sounds like there isn't.

> > +
> > +       /* TODO: if parent is a switch, this will fail. */
> 
> Won't the parent be a switch in all cases? For example, even in QEMU
> today the parent of the CXL device is the switch in the host bridge.
> 
> # cat /sys/bus/cxl/devices/port1/decoder1.0/devtype
> cxl_decoder_switch

In QEMU (and I assume hardware) they aren't the same, root ports have a separate
implementation from switches. That aside, the primary difference in the driver
is that cxl_acpi enumerates root ports so the cxl_mem driver can determine
connectedness as long as cxl_acpi has run.

> 
> > +       port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
> > +       if (!port_dev)
> > +               return -ENODEV;
> > +
> > +       return 0;
> >  }
> >
> >  static void cxl_mem_remove(struct device *dev)
> > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> > index 6931885c83ce..244b99948c40 100644
> > --- a/drivers/cxl/pci.c
> > +++ b/drivers/cxl/pci.c
> > @@ -335,29 +335,6 @@ static void cxl_pci_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
> >         pci_iounmap(to_pci_dev(cxlm->dev), base);
> >  }
> >
> > -static int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> > -{
> > -       int pos;
> > -
> > -       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> > -       if (!pos)
> > -               return 0;
> > -
> > -       while (pos) {
> > -               u16 vendor, id;
> > -
> > -               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> > -               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> > -               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> > -                       return pos;
> > -
> > -               pos = pci_find_next_ext_capability(pdev, pos,
> > -                                                  PCI_EXT_CAP_ID_DVSEC);
> > -       }
> > -
> > -       return 0;
> > -}
> > -
> >  static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
> >                           struct cxl_register_map *map)
> >  {
> > diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
> > index 8c1a58813816..d6b9978d05b0 100644
> > --- a/drivers/cxl/pci.h
> > +++ b/drivers/cxl/pci.h
> > @@ -11,7 +11,10 @@
> >   */
> >  #define PCI_DVSEC_HEADER1_LENGTH_MASK  GENMASK(31, 20)
> >  #define PCI_DVSEC_VENDOR_ID_CXL                0x1E98
> > -#define PCI_DVSEC_ID_CXL               0x0
> > +
> > +#define PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID   0x0
> > +#define PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET      0xC
> > +#define   CXL_PCIE_MEM_ENABLE                  BIT(2)
> >
> >  #define PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID       0x8
> >  #define PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET  0xC
> > @@ -29,4 +32,6 @@
> >
> >  #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
> >
> > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
> > +
> >  #endif /* __CXL_PCI_H__ */
> > --
> > 2.33.0
> >

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 13/13] cxl/mem: Enumerate switch decoders
  2021-09-03 17:56   ` Jonathan Cameron
@ 2021-09-13 22:12     ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 22:12 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-cxl, Alison Schofield, Dan Williams, Ira Weiny, Vishal Verma

On 21-09-03 18:56:23, Jonathan Cameron wrote:
> On Thu, 2 Sep 2021 12:50:17 -0700
> Ben Widawsky <ben.widawsky@intel.com> wrote:
> 
> > Switches work much in the same way as hostbridges. The primary
> > difference is that they are enumerated, and probed via regular PCIe
> > mechanisms. A switch has 1 upstream port, and n downstream ports.
> > Ultimately a memory device attached to a switch can determine if it's in
> > a CXL capable subset of the topology if the switch is CXL capable.
> > 
> > The algorithm introduced enables enumerating switches in a CXL topology.
> > It walks up the topology until it finds a root port (which is enumerated
> > by the cxl_acpi driver). Once at the top, it walks back down adding all
> > downstream ports along the way.
> > 
> > Note that practically speaking there can be at most 3 levels of switches
> > with the current 2.0 spec. This is because there is a max interleave of
> > 8 defined in the spec. If there is a single hostbridge and only 1 root
> > port was CXL capable, you could have 3 levels of x2 switches, making
> > the x8 interleave. However, as far as the spec is concerned, there can
> > be infinite number of switches since a x1 switch is allowed, and
> > future versions of the spec may allow for a larger total interleave.
> 
> Or you could be lazy and rely on the statement in CXL 2.0 that it supports
> only a single level of switching (search for "single level" in 1.4.1)
> Lots of other reasons it's far from infinite... (number of busses etc).
> 
> I'll not speculate on what might be supported in the future.

I like lazy, however, there is no statement in the spec that disallows multiple
levels of switches.

> 
> A few minor comments below.
> 
> Jonathan
> 
> > 
> > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > ---
> >  drivers/cxl/mem.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-
> >  drivers/cxl/pci.c |   8 ---
> >  drivers/cxl/pci.h |   8 +++
> >  3 files changed, 137 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > index aba9a07d519f..dc8ca43d5bfc 100644
> > --- a/drivers/cxl/mem.c
> > +++ b/drivers/cxl/mem.c
> > @@ -56,6 +56,133 @@ static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> >  	return true;
> >  }
> >  
> > +/* TODO: dedeuplicate this from drivers/cxl/pci.c? */
> 
> That seems like a question with an obvious answer...
> 
> > +static unsigned long get_component_regs(struct pci_dev *pdev)
> > +{
> > +	unsigned long component_reg_phys = CXL_RESOURCE_NONE;
> > +	u32 regloc_size, regblocks;
> > +	int regloc, i;
> > +
> > +	regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
> > +	if (!regloc) {
> > +		dev_err(&pdev->dev, "register location dvsec not found\n");
> > +		return component_reg_phys;
> > +	}
> > +
> > +	/* Get the size of the Register Locator DVSEC */
> > +	pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
> > +	regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
> > +
> > +	regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
> > +	regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
> > +
> > +	for (i = 0; i < regblocks; i++, regloc += 8) {
> > +		u32 reg_lo, reg_hi;
> > +		u8 reg_type;
> > +		u64 offset;
> > +		u8 bar;
> > +
> > +		pci_read_config_dword(pdev, regloc, &reg_lo);
> > +		pci_read_config_dword(pdev, regloc + 4, &reg_hi);
> > +
> > +		cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,
> > +					  &reg_type);
> > +
> > +		if (reg_type != CXL_REGLOC_RBI_COMPONENT)
> > +			continue;
> > +
> > +		component_reg_phys = pci_resource_start(pdev, bar) + offset;
> > +	}
> > +
> > +	return component_reg_phys;
> > +}
> > +
> > +static void enumerate_uport(struct device *dev)
> > +{
> > +	struct pci_dev *pdev = to_pci_dev(dev);
> > +
> > +	/*
> > +	 * Parent's parent should be another uport, since we don't have root
> > +	 * ports here
> > +	 */
> > +	if (dev_WARN_ONCE(dev, !dev->parent->parent, "No grandparent port\n"))
> > +		return;
> > +
> > +	if (!is_cxl_port(dev->parent->parent)) {
> > +		dev_info(dev, "Parent of uport isn't a CXL port (%s)\n",
> > +			 dev_name(dev->parent->parent));
> > +		return;
> > +	}
> > +
> > +	devm_cxl_add_port(dev, dev, get_component_regs(pdev),
> > +			  to_cxl_port(dev->parent));
> > +}
> > +
> > +static void enumerate_dport(struct device *dev)
> > +{
> > +	struct pci_dev *pdev = to_pci_dev(dev);
> > +	u32 port_num, lnkcap;
> > +
> > +	if (dev_WARN_ONCE(dev, !dev->parent, "No parent port\n"))
> > +		return;
> > +
> > +	if (!is_cxl_port(dev->parent)) {
> > +		dev_info(dev, "Uport isn't a CXL port %s\n",
> > +			 dev_name(dev->parent));
> > +		return;
> > +	}
> > +
> > +	/* TODO: deduplicate from drivers/cxl/acpi.c? */
> > +	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
> > +				  &lnkcap) != PCIBIOS_SUCCESSFUL)
> > +		return;
> > +	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
> > +
> > +	cxl_add_dport(to_cxl_port(dev->parent), dev, port_num,
> > +		      get_component_regs(pdev));
> > +}
> > +
> > +/*
> > + * Walk up the topology until we get to the root port (ie. parent is a
> > + * cxl port). From there walk back down adding the additional ports. If the
> > + * parent isn't a PCIe switch (upstream or downstream port), the downstream
> > + * endpoint(s) cannot be CXL enabled.
> > + *
> > + * XXX: It's possible that cxl_acpi hasn't yet enumerated the root ports, and
> > + * so that will rescan the CXL bus, thus coming back here.
> > + */
> > +static void enumerate_switches(struct device *dev)
> > +{
> > +	struct pci_dev *pdev;
> > +	int type;
> > +
> > +	if (unlikely(!dev))
> 
> Unlikely markings seems unlikely to be necessary. I'm assuming
> this is far from a hot path!
> 
> > +		return;
> > +
> > +	if (unlikely(!dev_is_pci(dev)))
> > +		return;
> > +
> > +	pdev = to_pci_dev(dev);
> > +
> > +	if (unlikely(!pci_is_pcie(pdev)))
> > +		return;
> > +
> > +	if (!is_cxl_mem_enabled(pdev))
> > +		return;
> > +
> > +	type = pci_pcie_type(pdev);
> > +
> > +	if (type != PCI_EXP_TYPE_UPSTREAM && type != PCI_EXP_TYPE_DOWNSTREAM)
> > +		return;
> > +
> > +	enumerate_switches(dev->parent);
> > +
> > +	if (type == PCI_EXP_TYPE_UPSTREAM)
> > +		enumerate_uport(dev);
> > +	if (type == PCI_EXP_TYPE_DOWNSTREAM)
> > +		enumerate_dport(dev);
> > +}
> > +
> >  static int cxl_mem_probe(struct device *dev)
> >  {
> >  	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> > @@ -68,7 +195,8 @@ static int cxl_mem_probe(struct device *dev)
> >  	if (!is_cxl_mem_enabled(pdev))
> >  		return -ENODEV;
> >  
> > -	/* TODO: if parent is a switch, this will fail. */
> > +	enumerate_switches(dev->parent);
> > +
> >  	port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
> >  	if (!port_dev)
> >  		return -ENODEV;
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-13 22:07       ` Dan Williams
@ 2021-09-13 23:19         ` Ben Widawsky
  2021-09-14 21:16           ` Dan Williams
  0 siblings, 1 reply; 60+ messages in thread
From: Ben Widawsky @ 2021-09-13 23:19 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-13 15:07:44, Dan Williams wrote:
> On Mon, Sep 13, 2021 at 9:11 AM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > On 21-09-10 12:19:24, Dan Williams wrote:
> > > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > > >
> > > > Endpoints have decoders too. It is useful to share the same
> > > > infrastructure from cxl_core. Endpoints do not have dports (downstream
> > > > targets), only the underlying physical medium. As a result, some special
> > > > casing is needed.
> > > >
> > > > There is no functional change introduced yet as endpoints don't actually
> > > > enumerate decoders yet.
> > > >
> > > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > > ---
> > > >  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
> > > >  1 file changed, 25 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > > index 8d5061b0794d..6202ce5a5ac2 100644
> > > > --- a/drivers/cxl/core/bus.c
> > > > +++ b/drivers/cxl/core/bus.c
> > > > @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
> > > >         NULL,
> > > >  };
> > > >
> > > > +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> > > > +       &cxl_decoder_base_attribute_group,
> > > > +       &cxl_base_attribute_group,
> > > > +       NULL,
> > > > +};
> > > > +
> > > >  static void cxl_decoder_release(struct device *dev)
> > > >  {
> > > >         struct cxl_decoder *cxld = to_cxl_decoder(dev);
> > > > @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
> > > >         kfree(cxld);
> > > >  }
> > > >
> > > > +static const struct device_type cxl_decoder_endpoint_type = {
> > > > +       .name = "cxl_decoder_endpoint",
> > > > +       .release = cxl_decoder_release,
> > > > +       .groups = cxl_decoder_endpoint_attribute_groups,
> > > > +};
> > > > +
> > > >  static const struct device_type cxl_decoder_switch_type = {
> > > >         .name = "cxl_decoder_switch",
> > > >         .release = cxl_decoder_release,
> > > > @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
> > > >         .groups = cxl_decoder_root_attribute_groups,
> > > >  };
> > > >
> > > > +static bool is_endpoint_decoder(struct device *dev)
> > > > +{
> > > > +       return dev->type == &cxl_decoder_endpoint_type;
> > > > +}
> > > > +
> > > >  bool is_root_decoder(struct device *dev)
> > > >  {
> > > >         return dev->type == &cxl_decoder_root_type;
> > > > @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > > >         struct device *dev;
> > > >         int rc = 0;
> > > >
> > > > -       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> > > > +       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
> > > >                 return ERR_PTR(-EINVAL);
> > > >
> > > >         cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> > > > @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > > >         dev->parent = &port->dev;
> > > >         dev->bus = &cxl_bus_type;
> > > >
> > > > +       /* Endpoints don't have a target list */
> > > > +       if (nr_targets == 0)
> > > > +               dev->type = &cxl_decoder_endpoint_type;
> > >
> > > Do you also plan to introduce the concept of endpoint ports, and if
> > > yes should that come before this patch? That would seem to be more
> > > robust than, for example, allowing a switch port to carry an endpoint
> > > decoder object as this allows.
> >
> > I didn't see a need as of yet to differentiate between endpoint ports and other
> > ports. I don't entirely understand what you mean by "allowing a switch port to
> > carry an endpoint decoder" means. Can you please elaborate?
> 
> If endpoint ports were an explicit type then this check could make
> sure that someone did not pass nr_targets set to 0 where the @port
> argument is referring to a switch where the target_list must be
> specified.
> 
> Either that, or a comment in kernel-doc for this routine about the
> special meaning of nr_targets == 0 and expected usage.

Well, since Jonathan also brought up a concern here perhaps I should entertain
other ideas. I suppose future versions of the spec could break things, but as it
stands today the only CXL component that implements decoders that can have a 0
value for this are endpoint devices (T2 or T3, or LD). I think it's fine to wait
until we have a second reason to make an endpoint port type and update kdocs for
now, but maybe it will also be a natural fit with a proper port driver.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 04/13] cxl: Introduce endpoint decoders
  2021-09-13 23:19         ` Ben Widawsky
@ 2021-09-14 21:16           ` Dan Williams
  0 siblings, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-14 21:16 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Mon, Sep 13, 2021 at 4:19 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> On 21-09-13 15:07:44, Dan Williams wrote:
> > On Mon, Sep 13, 2021 at 9:11 AM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > >
> > > On 21-09-10 12:19:24, Dan Williams wrote:
> > > > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > > > >
> > > > > Endpoints have decoders too. It is useful to share the same
> > > > > infrastructure from cxl_core. Endpoints do not have dports (downstream
> > > > > targets), only the underlying physical medium. As a result, some special
> > > > > casing is needed.
> > > > >
> > > > > There is no functional change introduced yet as endpoints don't actually
> > > > > enumerate decoders yet.
> > > > >
> > > > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > > > ---
> > > > >  drivers/cxl/core/bus.c | 29 +++++++++++++++++++++++++----
> > > > >  1 file changed, 25 insertions(+), 4 deletions(-)
> > > > >
> > > > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > > > index 8d5061b0794d..6202ce5a5ac2 100644
> > > > > --- a/drivers/cxl/core/bus.c
> > > > > +++ b/drivers/cxl/core/bus.c
> > > > > @@ -175,6 +175,12 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = {
> > > > >         NULL,
> > > > >  };
> > > > >
> > > > > +static const struct attribute_group *cxl_decoder_endpoint_attribute_groups[] = {
> > > > > +       &cxl_decoder_base_attribute_group,
> > > > > +       &cxl_base_attribute_group,
> > > > > +       NULL,
> > > > > +};
> > > > > +
> > > > >  static void cxl_decoder_release(struct device *dev)
> > > > >  {
> > > > >         struct cxl_decoder *cxld = to_cxl_decoder(dev);
> > > > > @@ -184,6 +190,12 @@ static void cxl_decoder_release(struct device *dev)
> > > > >         kfree(cxld);
> > > > >  }
> > > > >
> > > > > +static const struct device_type cxl_decoder_endpoint_type = {
> > > > > +       .name = "cxl_decoder_endpoint",
> > > > > +       .release = cxl_decoder_release,
> > > > > +       .groups = cxl_decoder_endpoint_attribute_groups,
> > > > > +};
> > > > > +
> > > > >  static const struct device_type cxl_decoder_switch_type = {
> > > > >         .name = "cxl_decoder_switch",
> > > > >         .release = cxl_decoder_release,
> > > > > @@ -196,6 +208,11 @@ static const struct device_type cxl_decoder_root_type = {
> > > > >         .groups = cxl_decoder_root_attribute_groups,
> > > > >  };
> > > > >
> > > > > +static bool is_endpoint_decoder(struct device *dev)
> > > > > +{
> > > > > +       return dev->type == &cxl_decoder_endpoint_type;
> > > > > +}
> > > > > +
> > > > >  bool is_root_decoder(struct device *dev)
> > > > >  {
> > > > >         return dev->type == &cxl_decoder_root_type;
> > > > > @@ -472,7 +489,7 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > > > >         struct device *dev;
> > > > >         int rc = 0;
> > > > >
> > > > > -       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
> > > > > +       if (nr_targets > CXL_DECODER_MAX_INTERLEAVE)
> > > > >                 return ERR_PTR(-EINVAL);
> > > > >
> > > > >         cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
> > > > > @@ -491,8 +508,11 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
> > > > >         dev->parent = &port->dev;
> > > > >         dev->bus = &cxl_bus_type;
> > > > >
> > > > > +       /* Endpoints don't have a target list */
> > > > > +       if (nr_targets == 0)
> > > > > +               dev->type = &cxl_decoder_endpoint_type;
> > > >
> > > > Do you also plan to introduce the concept of endpoint ports, and if
> > > > yes should that come before this patch? That would seem to be more
> > > > robust than, for example, allowing a switch port to carry an endpoint
> > > > decoder object as this allows.
> > >
> > > I didn't see a need as of yet to differentiate between endpoint ports and other
> > > ports. I don't entirely understand what you mean by "allowing a switch port to
> > > carry an endpoint decoder" means. Can you please elaborate?
> >
> > If endpoint ports were an explicit type then this check could make
> > sure that someone did not pass nr_targets set to 0 where the @port
> > argument is referring to a switch where the target_list must be
> > specified.
> >
> > Either that, or a comment in kernel-doc for this routine about the
> > special meaning of nr_targets == 0 and expected usage.
>
> Well, since Jonathan also brought up a concern here perhaps I should entertain
> other ideas. I suppose future versions of the spec could break things, but as it
> stands today the only CXL component that implements decoders that can have a 0
> value for this are endpoint devices (T2 or T3, or LD). I think it's fine to wait
> until we have a second reason to make an endpoint port type and update kdocs for
> now, but maybe it will also be a natural fit with a proper port driver.

"Document only" sounds good to me.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-13 22:10     ` Ben Widawsky
@ 2021-09-14 22:42       ` Dan Williams
  2021-09-14 22:55         ` Ben Widawsky
  0 siblings, 1 reply; 60+ messages in thread
From: Dan Williams @ 2021-09-14 22:42 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Mon, Sep 13, 2021 at 3:10 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> On 21-09-10 14:59:29, Dan Williams wrote:
> > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > >
> > > If the "upstream" port of the endpoint is an enumerated downstream CXL
> > > port, and the device itself is CXL capable and enabled, the memdev
> > > driver can bind. This binding useful for region configuration/creation
> > > because it provides a clean way for the region code to determine if the
> > > memdev is actually CXL capable.
> > >
> > > A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> > > the end of ACPI probing (see comment in code for details). Switch
> > > enumeration will be done as a follow-on patch. As a result, if a switch
> > > is in the topology the memdev driver will not bind to any devices.
> > >
> > > CXL.mem capability is checked lazily at the time a region is bound.
> > > This is in line with the other configuration parameters.
> > >
> > > Below is an example (mem0, and mem1) of CXL memdev devices that now
> > > exist on the bus.
> > >
> > > /sys/bus/cxl/devices/
> > > ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> > > ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> > > ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
> >
> > I'm confused, this isn't showing anything new that did not already
> > exist before this patch? What I think would be a useful shortcut is
> > for memX devices to have an attribute that links back to their cxl
> > root port after validation completes. Like an attribute group that
> > arrives and disappears when the driver successfully binds and unbinds
> > respectively.
> >
>
> This was a copy-paste mistake. I meant to show the memX devices under cxl_mem
> drivers, like this:
> # tree /sys/bus/cxl/drivers/
> /sys/bus/cxl/drivers/
> ├── cxl_mem
> │   ├── bind
> │   ├── mem0 -> ../../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem0
> │   ├── mem1 -> ../../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem1

Ok, but that's still just typical sysfs, the new ground to highlight
in my mind would be the path of the port added by the memX driver
appearing in the hierarchy of CXL ports up to the host bridge from the
device.

> │   ├── uevent
> │   └── unbind
> ├── cxl_nvdimm
> │   ├── bind
> │   ├── uevent
> │   └── unbind
> └── cxl_nvdimm_bridge
>     ├── bind
>     ├── uevent
>     └── unbind
>
>
> While I'm not opposed to add a link as you mention, I don't yet see the utility.
> Are you thinking as primarily a convenience for userspace tooling? Is this
> useful if you have a switch in the path?

I was thinking it is useful for something like:

cxl list --memdevs --root-decoder=decoder0.0

...where it filters all the memdev by the ones that can possibly
participate in a given CFMWS range. Without a cache-copy of the root
port hanging off of the memdev, cxl-cli will need to redo the work the
kernel has already done to walk the CXL connectivity topology

This capability to list devices by their relationship to other objects
proved powerful for "ndctl list".

>
> > > ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> > > ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> > > ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> > > └── root0 -> ../../../devices/platform/ACPI0017:00/root0
> > >
> > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > ---
> > >  drivers/cxl/acpi.c        | 27 +++++++-----------
> > >  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
> > >  drivers/cxl/core/memdev.c |  6 ++++
> > >  drivers/cxl/cxl.h         |  2 ++
> > >  drivers/cxl/cxlmem.h      |  2 ++
> > >  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
> > >  drivers/cxl/pci.c         | 23 ---------------
> > >  drivers/cxl/pci.h         |  7 ++++-
> > >  8 files changed, 141 insertions(+), 41 deletions(-)
> > >
> > > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > > index 7130beffc929..fd14094bdb3f 100644
> > > --- a/drivers/cxl/acpi.c
> > > +++ b/drivers/cxl/acpi.c
> > > @@ -240,21 +240,6 @@ __mock 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;
> > > -}
> > > -
> > >  __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> > >                                               struct device *dev)
> > >  {
> > > @@ -459,9 +444,19 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> > >         if (rc)
> > >                 goto out;
> > >
> > > -       if (IS_ENABLED(CONFIG_CXL_PMEM))
> > > +       if (IS_ENABLED(CONFIG_CXL_PMEM)) {
> > >                 rc = device_for_each_child(&root_port->dev, root_port,
> > >                                            add_root_nvdimm_bridge);
> > > +               if (rc)
> > > +                       goto out;
> > > +       }
> > > +
> > > +       /*
> > > +        * While ACPI is scanning hostbridge ports, switches and memory devices
> > > +        * may have been probed. Those devices will need to know whether the
> > > +        * hostbridge is CXL capable.
> > > +        */
> > > +       rc = bus_rescan_devices(&cxl_bus_type);
> >
> > I don't think it's a good idea to call bus_rescan_devices() from
> > probe() context. This now sets up a lockdep dependency between the
> > ACPI0017 device-lock and all the device-locks for every device on the
> > cxl-bus. This is why the nvdimm code punts the rescan outside the lock
> > to a workqueue.
> >
> > Lockdep unfortunately won't complain about device-lock entanglements.
> > One item for the backlog is to add device-lock validation to the cxl
> > subsystem ala commit 87a30e1f05d7 ("driver-core, libnvdimm: Let device
> > subsystems add local lockdep coverage")
>
> Good catch. I will fix. This is now the second time I tripped over that backlog
> item :-)
>
> >
> >
> > >
> > >  out:
> > >         acpi_put_table(acpi_cedt);
> > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > index 256e55dc2a3b..56f57302d27b 100644
> > > --- a/drivers/cxl/core/bus.c
> > > +++ b/drivers/cxl/core/bus.c
> > > @@ -8,6 +8,7 @@
> > >  #include <linux/idr.h>
> > >  #include <cxlmem.h>
> > >  #include <cxl.h>
> > > +#include <pci.h>
> > >  #include "core.h"
> > >
> > >  /**
> > > @@ -259,6 +260,12 @@ static const struct device_type cxl_port_type = {
> > >         .groups = cxl_port_attribute_groups,
> > >  };
> > >
> > > +bool is_cxl_port(struct device *dev)
> > > +{
> > > +       return dev->type == &cxl_port_type;
> > > +}
> > > +EXPORT_SYMBOL_GPL(is_cxl_port);
> > > +
> > >  struct cxl_port *to_cxl_port(struct device *dev)
> > >  {
> > >         if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
> > > @@ -266,6 +273,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
> > >                 return NULL;
> > >         return container_of(dev, struct cxl_port, dev);
> > >  }
> > > +EXPORT_SYMBOL_GPL(to_cxl_port);
> > >
> > >  static void unregister_port(void *_port)
> > >  {
> > > @@ -424,6 +432,27 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
> > >         return dup ? -EEXIST : 0;
> > >  }
> > >
> > > +/**
> > > + * find_dport_by_dev - gets downstream CXL port from a struct device
> > > + * @port: cxl [upstream] port that "owns" the downstream port is being queried
> > > + * @dev: The device that is backing the downstream port
> > > + */
> > > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const 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;
> > > +}
> > > +EXPORT_SYMBOL_GPL(find_dport_by_dev);
> >
> > This wants to move into the "cxl_" prefix symbol namespace if it's now
> > going to be a public function.
> >
> > > +
> > >  /**
> > >   * cxl_add_dport - append downstream port data to a cxl_port
> > >   * @port: the cxl_port that references this dport
> > > @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
> > >  }
> > >  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
> > >
> > > +/**
> > > + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> > > + * @pdev: PCI device to search for the DVSEC
> > > + * @dvsec: DVSEC id to look for
> > > + *
> > > + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> > > + */
> > > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> > > +{
> > > +       int pos;
> > > +
> > > +       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> > > +       if (!pos)
> > > +               return 0;
> > > +
> > > +       while (pos) {
> > > +               u16 vendor, id;
> > > +
> > > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> > > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> > > +               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> > > +                       return pos;
> > > +
> > > +               pos = pci_find_next_ext_capability(pdev, pos,
> > > +                                                  PCI_EXT_CAP_ID_DVSEC);
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);
> >
> > Why not keep this enumeration in cxl_pci and have it record the
> > component register block base address at cxl_memdev creation time?
> > This would make it similar to cxl_port creation that takes a
> > component_register base address argument.
> >
>
> It does that, this is needed in addition to that to find certain DVSEC registers
> that are used to determine CXL properties.

Ok, like HDM Base registers?

Should this information be passed in to the port creation rather than
retrieved after port creation?

Side tangent... is drivers/cxl/core/bus.c becoming a dumping ground?
I.e. is it time for drivers/cxl/core/{pci,port}.c?

>
> > > +
> > >  /**
> > >   * __cxl_driver_register - register a driver for the cxl bus
> > >   * @cxl_drv: cxl driver structure to attach
> > > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > > index c9dd054bd813..0068b5ff5f3e 100644
> > > --- a/drivers/cxl/core/memdev.c
> > > +++ b/drivers/cxl/core/memdev.c
> > > @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
> > >  {
> > >         unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
> > >  }
> > > +
> > > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> > > +{
> > > +       return !!cxlmd->dev.driver;
> > > +}
> > > +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
> >
> > Perhaps:
> >
> > s/capable/{enabled,routed}/
> >
> > The device is always capable, it's the hierarchy that will let it down.
>
> I can rename to routed. From my perspective, capable and enabled aren't
> synonymous. A capable device may not be enabled.
>
> >
> > > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > > index b48bdbefd949..a168520d741b 100644
> > > --- a/drivers/cxl/cxl.h
> > > +++ b/drivers/cxl/cxl.h
> > > @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> > >                                    resource_size_t component_reg_phys,
> > >                                    struct cxl_port *parent_port);
> > >
> > > +bool is_cxl_port(struct device *dev);
> > >  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
> > >                   resource_size_t component_reg_phys);
> > > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
> > >
> > >  struct cxl_decoder *to_cxl_decoder(struct device *dev);
> > >  bool is_root_decoder(struct device *dev);
> > > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > > index 811b24451604..88264204c4b9 100644
> > > --- a/drivers/cxl/cxlmem.h
> > > +++ b/drivers/cxl/cxlmem.h
> > > @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> > >  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> > >                                        struct cxl_mem *cxlm);
> > >
> > > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> > > +
> > >  /**
> > >   * struct cxl_mbox_cmd - A command to be submitted to hardware.
> > >   * @opcode: (input) The command set and command submitted to hardware.
> > > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > > index 978a54b0a51a..b6dc34d18a86 100644
> > > --- a/drivers/cxl/mem.c
> > > +++ b/drivers/cxl/mem.c
> > > @@ -2,8 +2,10 @@
> > >  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> > >  #include <linux/device.h>
> > >  #include <linux/module.h>
> > > +#include <linux/pci.h>
> > >
> > >  #include "cxlmem.h"
> > > +#include "pci.h"
> > >
> > >  /**
> > >   * DOC: cxl mem
> > > @@ -17,9 +19,60 @@
> > >   * components.
> > >   */
> > >
> > > +static int port_match(struct device *dev, const void *data)
> > > +{
> > > +       struct cxl_port *port;
> > > +
> > > +       if (!is_cxl_port(dev))
> > > +               return 0;
> > > +
> > > +       port = to_cxl_port(dev);
> > > +
> > > +       if (find_dport_by_dev(port, (struct device *)data))
> > > +               return 1;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> > > +{
> > > +       int pcie_dvsec;
> > > +       u16 dvsec_ctrl;
> > > +
> > > +       pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> > > +       if (!pcie_dvsec) {
> > > +               dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> > > +               return false;
> > > +       }
> > > +
> > > +       pci_read_config_word(pdev,
> > > +                            pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> > > +                            &dvsec_ctrl);
> > > +       if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> > > +               dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
> > > +               return false;
> > > +       }
> > > +
> > > +       return true;
> > > +}
> > > +
> > >  static int cxl_mem_probe(struct device *dev)
> > >  {
> > > -       return -EOPNOTSUPP;
> > > +       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> > > +       struct cxl_mem *cxlm = cxlmd->cxlm;
> > > +       struct device *pdev_parent = cxlm->dev->parent;
> > > +       struct pci_dev *pdev = to_pci_dev(cxlm->dev);
> >
> > It's not safe to assume that the parent of a cxlmd is a pci device.
> >
>
> What can it be then? Isn't the parent always going to be a downstream port,
> root port, or emulated port from cxl_test?

It was a nice property of the recent reworks that
drivers/cxl/core/memdev.c was able to drop its include of
<linux/pci.h> because drivers/cxl/pci.c handled all of those details.
So I'd prefer that any extra PCI details that memdev needs be probed
by the PCI driver and passed to the creation of the memdev device,
like the component register location.

This preserves the ability of cxl_test to implement the ioctl and
sysfs ABI without needing to also emulate PCI, but also just keeps PCI
concerns in files named "pci".

>
> > > +       struct device *port_dev;
> > > +
> > > +       if (!is_cxl_mem_enabled(pdev))
> > > +               return -ENODEV;
> >
> > This isn't sufficient, this needs to walk the entire hierarchy, right?
> >
>
> It was saved to a later patch because I have no way to actually test deeper
> hierarchies at the moment. After I had already sent this, you and I discussed
> not doing that. I can merge it together if there's no perceived value in keeping
> them separate, which it sounds like there isn't.

Yeah, let's keep heading that direction...

>
> > > +
> > > +       /* TODO: if parent is a switch, this will fail. */
> >
> > Won't the parent be a switch in all cases? For example, even in QEMU
> > today the parent of the CXL device is the switch in the host bridge.
> >
> > # cat /sys/bus/cxl/devices/port1/decoder1.0/devtype
> > cxl_decoder_switch
>
> In QEMU (and I assume hardware) they aren't the same, root ports have a separate
> implementation from switches. That aside, the primary difference in the driver
> is that cxl_acpi enumerates root ports so the cxl_mem driver can determine
> connectedness as long as cxl_acpi has run.

I know that PCI root ports are distinct from PCI switches, but the
Linux CXL topology is a Linux specific interpretation of the CXL spec
where cxl_decoder_switch applies equally to the 2nd-level+ decoders.
There is no cxl_decoder_root_port. So the comment on a minimum either
needs to be deleted or fixed up to be specific about
Linux-cxl_decoder_switch vs PCI/CXL-spec defined switch.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 07/13] cxl/memdev: Determine CXL.mem capability
  2021-09-14 22:42       ` Dan Williams
@ 2021-09-14 22:55         ` Ben Widawsky
  0 siblings, 0 replies; 60+ messages in thread
From: Ben Widawsky @ 2021-09-14 22:55 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On 21-09-14 15:42:01, Dan Williams wrote:
> On Mon, Sep 13, 2021 at 3:10 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> >
> > On 21-09-10 14:59:29, Dan Williams wrote:
> > > On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
> > > >
> > > > If the "upstream" port of the endpoint is an enumerated downstream CXL
> > > > port, and the device itself is CXL capable and enabled, the memdev
> > > > driver can bind. This binding useful for region configuration/creation
> > > > because it provides a clean way for the region code to determine if the
> > > > memdev is actually CXL capable.
> > > >
> > > > A memdev/hostbridge probe race is solved with a full CXL bus rescan at
> > > > the end of ACPI probing (see comment in code for details). Switch
> > > > enumeration will be done as a follow-on patch. As a result, if a switch
> > > > is in the topology the memdev driver will not bind to any devices.
> > > >
> > > > CXL.mem capability is checked lazily at the time a region is bound.
> > > > This is in line with the other configuration parameters.
> > > >
> > > > Below is an example (mem0, and mem1) of CXL memdev devices that now
> > > > exist on the bus.
> > > >
> > > > /sys/bus/cxl/devices/
> > > > ├── decoder0.0 -> ../../../devices/platform/ACPI0017:00/root0/decoder0.0
> > > > ├── mem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0
> > > > ├── mem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1
> > >
> > > I'm confused, this isn't showing anything new that did not already
> > > exist before this patch? What I think would be a useful shortcut is
> > > for memX devices to have an attribute that links back to their cxl
> > > root port after validation completes. Like an attribute group that
> > > arrives and disappears when the driver successfully binds and unbinds
> > > respectively.
> > >
> >
> > This was a copy-paste mistake. I meant to show the memX devices under cxl_mem
> > drivers, like this:
> > # tree /sys/bus/cxl/drivers/
> > /sys/bus/cxl/drivers/
> > ├── cxl_mem
> > │   ├── bind
> > │   ├── mem0 -> ../../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem0
> > │   ├── mem1 -> ../../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem1
> 
> Ok, but that's still just typical sysfs, the new ground to highlight
> in my mind would be the path of the port added by the memX driver
> appearing in the hierarchy of CXL ports up to the host bridge from the
> device.

Okay. I assume core is going to need to grow an API to do that. Something like:
cxl_memdev_sysfs_rescan()?

> 
> > │   ├── uevent
> > │   └── unbind
> > ├── cxl_nvdimm
> > │   ├── bind
> > │   ├── uevent
> > │   └── unbind
> > └── cxl_nvdimm_bridge
> >     ├── bind
> >     ├── uevent
> >     └── unbind
> >
> >
> > While I'm not opposed to add a link as you mention, I don't yet see the utility.
> > Are you thinking as primarily a convenience for userspace tooling? Is this
> > useful if you have a switch in the path?
> 
> I was thinking it is useful for something like:
> 
> cxl list --memdevs --root-decoder=decoder0.0
> 
> ...where it filters all the memdev by the ones that can possibly
> participate in a given CFMWS range. Without a cache-copy of the root
> port hanging off of the memdev, cxl-cli will need to redo the work the
> kernel has already done to walk the CXL connectivity topology
> 
> This capability to list devices by their relationship to other objects
> proved powerful for "ndctl list".
> 

I find it hard to believe at this point that the tool won't need to walk the
topology anyway. It's a good goal however, so I'm in.

> >
> > > > ├── pmem0 -> ../../../devices/pci0000:34/0000:34:01.0/0000:36:00.0/mem0/pmem0
> > > > ├── pmem1 -> ../../../devices/pci0000:34/0000:34:00.0/0000:35:00.0/mem1/pmem1
> > > > ├── port1 -> ../../../devices/platform/ACPI0017:00/root0/port1
> > > > └── root0 -> ../../../devices/platform/ACPI0017:00/root0
> > > >
> > > > Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> > > > ---
> > > >  drivers/cxl/acpi.c        | 27 +++++++-----------
> > > >  drivers/cxl/core/bus.c    | 60 +++++++++++++++++++++++++++++++++++++++
> > > >  drivers/cxl/core/memdev.c |  6 ++++
> > > >  drivers/cxl/cxl.h         |  2 ++
> > > >  drivers/cxl/cxlmem.h      |  2 ++
> > > >  drivers/cxl/mem.c         | 55 ++++++++++++++++++++++++++++++++++-
> > > >  drivers/cxl/pci.c         | 23 ---------------
> > > >  drivers/cxl/pci.h         |  7 ++++-
> > > >  8 files changed, 141 insertions(+), 41 deletions(-)
> > > >
> > > > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > > > index 7130beffc929..fd14094bdb3f 100644
> > > > --- a/drivers/cxl/acpi.c
> > > > +++ b/drivers/cxl/acpi.c
> > > > @@ -240,21 +240,6 @@ __mock 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;
> > > > -}
> > > > -
> > > >  __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> > > >                                               struct device *dev)
> > > >  {
> > > > @@ -459,9 +444,19 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> > > >         if (rc)
> > > >                 goto out;
> > > >
> > > > -       if (IS_ENABLED(CONFIG_CXL_PMEM))
> > > > +       if (IS_ENABLED(CONFIG_CXL_PMEM)) {
> > > >                 rc = device_for_each_child(&root_port->dev, root_port,
> > > >                                            add_root_nvdimm_bridge);
> > > > +               if (rc)
> > > > +                       goto out;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * While ACPI is scanning hostbridge ports, switches and memory devices
> > > > +        * may have been probed. Those devices will need to know whether the
> > > > +        * hostbridge is CXL capable.
> > > > +        */
> > > > +       rc = bus_rescan_devices(&cxl_bus_type);
> > >
> > > I don't think it's a good idea to call bus_rescan_devices() from
> > > probe() context. This now sets up a lockdep dependency between the
> > > ACPI0017 device-lock and all the device-locks for every device on the
> > > cxl-bus. This is why the nvdimm code punts the rescan outside the lock
> > > to a workqueue.
> > >
> > > Lockdep unfortunately won't complain about device-lock entanglements.
> > > One item for the backlog is to add device-lock validation to the cxl
> > > subsystem ala commit 87a30e1f05d7 ("driver-core, libnvdimm: Let device
> > > subsystems add local lockdep coverage")
> >
> > Good catch. I will fix. This is now the second time I tripped over that backlog
> > item :-)
> >
> > >
> > >
> > > >
> > > >  out:
> > > >         acpi_put_table(acpi_cedt);
> > > > diff --git a/drivers/cxl/core/bus.c b/drivers/cxl/core/bus.c
> > > > index 256e55dc2a3b..56f57302d27b 100644
> > > > --- a/drivers/cxl/core/bus.c
> > > > +++ b/drivers/cxl/core/bus.c
> > > > @@ -8,6 +8,7 @@
> > > >  #include <linux/idr.h>
> > > >  #include <cxlmem.h>
> > > >  #include <cxl.h>
> > > > +#include <pci.h>
> > > >  #include "core.h"
> > > >
> > > >  /**
> > > > @@ -259,6 +260,12 @@ static const struct device_type cxl_port_type = {
> > > >         .groups = cxl_port_attribute_groups,
> > > >  };
> > > >
> > > > +bool is_cxl_port(struct device *dev)
> > > > +{
> > > > +       return dev->type == &cxl_port_type;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(is_cxl_port);
> > > > +
> > > >  struct cxl_port *to_cxl_port(struct device *dev)
> > > >  {
> > > >         if (dev_WARN_ONCE(dev, dev->type != &cxl_port_type,
> > > > @@ -266,6 +273,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
> > > >                 return NULL;
> > > >         return container_of(dev, struct cxl_port, dev);
> > > >  }
> > > > +EXPORT_SYMBOL_GPL(to_cxl_port);
> > > >
> > > >  static void unregister_port(void *_port)
> > > >  {
> > > > @@ -424,6 +432,27 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
> > > >         return dup ? -EEXIST : 0;
> > > >  }
> > > >
> > > > +/**
> > > > + * find_dport_by_dev - gets downstream CXL port from a struct device
> > > > + * @port: cxl [upstream] port that "owns" the downstream port is being queried
> > > > + * @dev: The device that is backing the downstream port
> > > > + */
> > > > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const 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;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(find_dport_by_dev);
> > >
> > > This wants to move into the "cxl_" prefix symbol namespace if it's now
> > > going to be a public function.
> > >
> > > > +
> > > >  /**
> > > >   * cxl_add_dport - append downstream port data to a cxl_port
> > > >   * @port: the cxl_port that references this dport
> > > > @@ -596,6 +625,37 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
> > > >
> > > > +/**
> > > > + * cxl_pci_dvsec - Gets offset for the given DVSEC id
> > > > + * @pdev: PCI device to search for the DVSEC
> > > > + * @dvsec: DVSEC id to look for
> > > > + *
> > > > + * Return: offset within the PCI header for the given DVSEC id. 0 if not found
> > > > + */
> > > > +int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec)
> > > > +{
> > > > +       int pos;
> > > > +
> > > > +       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
> > > > +       if (!pos)
> > > > +               return 0;
> > > > +
> > > > +       while (pos) {
> > > > +               u16 vendor, id;
> > > > +
> > > > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
> > > > +               pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
> > > > +               if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
> > > > +                       return pos;
> > > > +
> > > > +               pos = pci_find_next_ext_capability(pdev, pos,
> > > > +                                                  PCI_EXT_CAP_ID_DVSEC);
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(cxl_mem_dvsec);
> > >
> > > Why not keep this enumeration in cxl_pci and have it record the
> > > component register block base address at cxl_memdev creation time?
> > > This would make it similar to cxl_port creation that takes a
> > > component_register base address argument.
> > >
> >
> > It does that, this is needed in addition to that to find certain DVSEC registers
> > that are used to determine CXL properties.
> 
> Ok, like HDM Base registers?
> 
> Should this information be passed in to the port creation rather than
> retrieved after port creation?

Current code is looking at fields in DVSEC actually. I'm fine though with the
goal of not having any PCI _stuff_ in mem.c and passing it along through
cxl_pci.

> 
> Side tangent... is drivers/cxl/core/bus.c becoming a dumping ground?
> I.e. is it time for drivers/cxl/core/{pci,port}.c?
> 

I think it's likely bus.c is bound to become a dumping ground to some extent.
This particular addition will go away as stated below. Probably regs.c could be
renamed to pci.c as a starting point when we cross the threshold.

> >
> > > > +
> > > >  /**
> > > >   * __cxl_driver_register - register a driver for the cxl bus
> > > >   * @cxl_drv: cxl driver structure to attach
> > > > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> > > > index c9dd054bd813..0068b5ff5f3e 100644
> > > > --- a/drivers/cxl/core/memdev.c
> > > > +++ b/drivers/cxl/core/memdev.c
> > > > @@ -337,3 +337,9 @@ void cxl_memdev_exit(void)
> > > >  {
> > > >         unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS);
> > > >  }
> > > > +
> > > > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd)
> > > > +{
> > > > +       return !!cxlmd->dev.driver;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(is_cxl_mem_capable);
> > >
> > > Perhaps:
> > >
> > > s/capable/{enabled,routed}/
> > >
> > > The device is always capable, it's the hierarchy that will let it down.
> >
> > I can rename to routed. From my perspective, capable and enabled aren't
> > synonymous. A capable device may not be enabled.
> >
> > >
> > > > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > > > index b48bdbefd949..a168520d741b 100644
> > > > --- a/drivers/cxl/cxl.h
> > > > +++ b/drivers/cxl/cxl.h
> > > > @@ -283,8 +283,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> > > >                                    resource_size_t component_reg_phys,
> > > >                                    struct cxl_port *parent_port);
> > > >
> > > > +bool is_cxl_port(struct device *dev);
> > > >  int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
> > > >                   resource_size_t component_reg_phys);
> > > > +struct cxl_dport *find_dport_by_dev(struct cxl_port *port, const struct device *dev);
> > > >
> > > >  struct cxl_decoder *to_cxl_decoder(struct device *dev);
> > > >  bool is_root_decoder(struct device *dev);
> > > > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> > > > index 811b24451604..88264204c4b9 100644
> > > > --- a/drivers/cxl/cxlmem.h
> > > > +++ b/drivers/cxl/cxlmem.h
> > > > @@ -51,6 +51,8 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
> > > >  struct cxl_memdev *devm_cxl_add_memdev(struct device *host,
> > > >                                        struct cxl_mem *cxlm);
> > > >
> > > > +bool is_cxl_mem_capable(struct cxl_memdev *cxlmd);
> > > > +
> > > >  /**
> > > >   * struct cxl_mbox_cmd - A command to be submitted to hardware.
> > > >   * @opcode: (input) The command set and command submitted to hardware.
> > > > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> > > > index 978a54b0a51a..b6dc34d18a86 100644
> > > > --- a/drivers/cxl/mem.c
> > > > +++ b/drivers/cxl/mem.c
> > > > @@ -2,8 +2,10 @@
> > > >  /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> > > >  #include <linux/device.h>
> > > >  #include <linux/module.h>
> > > > +#include <linux/pci.h>
> > > >
> > > >  #include "cxlmem.h"
> > > > +#include "pci.h"
> > > >
> > > >  /**
> > > >   * DOC: cxl mem
> > > > @@ -17,9 +19,60 @@
> > > >   * components.
> > > >   */
> > > >
> > > > +static int port_match(struct device *dev, const void *data)
> > > > +{
> > > > +       struct cxl_port *port;
> > > > +
> > > > +       if (!is_cxl_port(dev))
> > > > +               return 0;
> > > > +
> > > > +       port = to_cxl_port(dev);
> > > > +
> > > > +       if (find_dport_by_dev(port, (struct device *)data))
> > > > +               return 1;
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static bool is_cxl_mem_enabled(struct pci_dev *pdev)
> > > > +{
> > > > +       int pcie_dvsec;
> > > > +       u16 dvsec_ctrl;
> > > > +
> > > > +       pcie_dvsec = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_PCIE_DVSEC_CXL_DVSEC_ID);
> > > > +       if (!pcie_dvsec) {
> > > > +               dev_info(&pdev->dev, "Unable to determine CXL protocol support");
> > > > +               return false;
> > > > +       }
> > > > +
> > > > +       pci_read_config_word(pdev,
> > > > +                            pcie_dvsec + PCI_DVSEC_ID_CXL_PCIE_CTRL_OFFSET,
> > > > +                            &dvsec_ctrl);
> > > > +       if (!(dvsec_ctrl & CXL_PCIE_MEM_ENABLE)) {
> > > > +               dev_info(&pdev->dev, "CXL.mem protocol not supported on device");
> > > > +               return false;
> > > > +       }
> > > > +
> > > > +       return true;
> > > > +}
> > > > +
> > > >  static int cxl_mem_probe(struct device *dev)
> > > >  {
> > > > -       return -EOPNOTSUPP;
> > > > +       struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> > > > +       struct cxl_mem *cxlm = cxlmd->cxlm;
> > > > +       struct device *pdev_parent = cxlm->dev->parent;
> > > > +       struct pci_dev *pdev = to_pci_dev(cxlm->dev);
> > >
> > > It's not safe to assume that the parent of a cxlmd is a pci device.
> > >
> >
> > What can it be then? Isn't the parent always going to be a downstream port,
> > root port, or emulated port from cxl_test?
> 
> It was a nice property of the recent reworks that
> drivers/cxl/core/memdev.c was able to drop its include of
> <linux/pci.h> because drivers/cxl/pci.c handled all of those details.
> So I'd prefer that any extra PCI details that memdev needs be probed
> by the PCI driver and passed to the creation of the memdev device,
> like the component register location.
> 
> This preserves the ability of cxl_test to implement the ioctl and
> sysfs ABI without needing to also emulate PCI, but also just keeps PCI
> concerns in files named "pci".

Okay. As stated I will pass along the info from cxl_pci or cxl_test.

> 
> >
> > > > +       struct device *port_dev;
> > > > +
> > > > +       if (!is_cxl_mem_enabled(pdev))
> > > > +               return -ENODEV;
> > >
> > > This isn't sufficient, this needs to walk the entire hierarchy, right?
> > >
> >
> > It was saved to a later patch because I have no way to actually test deeper
> > hierarchies at the moment. After I had already sent this, you and I discussed
> > not doing that. I can merge it together if there's no perceived value in keeping
> > them separate, which it sounds like there isn't.
> 
> Yeah, let's keep heading that direction...
> 
> >
> > > > +
> > > > +       /* TODO: if parent is a switch, this will fail. */
> > >
> > > Won't the parent be a switch in all cases? For example, even in QEMU
> > > today the parent of the CXL device is the switch in the host bridge.
> > >
> > > # cat /sys/bus/cxl/devices/port1/decoder1.0/devtype
> > > cxl_decoder_switch
> >
> > In QEMU (and I assume hardware) they aren't the same, root ports have a separate
> > implementation from switches. That aside, the primary difference in the driver
> > is that cxl_acpi enumerates root ports so the cxl_mem driver can determine
> > connectedness as long as cxl_acpi has run.
> 
> I know that PCI root ports are distinct from PCI switches, but the
> Linux CXL topology is a Linux specific interpretation of the CXL spec
> where cxl_decoder_switch applies equally to the 2nd-level+ decoders.
> There is no cxl_decoder_root_port. So the comment on a minimum either
> needs to be deleted or fixed up to be specific about
> Linux-cxl_decoder_switch vs PCI/CXL-spec defined switch.

I can fix up the comment, but per your earlier request the TODO will go away.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH 13/13] cxl/mem: Enumerate switch decoders
  2021-09-02 19:50 ` [PATCH 13/13] cxl/mem: Enumerate switch decoders Ben Widawsky
  2021-09-03 17:56   ` Jonathan Cameron
@ 2021-09-14 23:31   ` Dan Williams
  1 sibling, 0 replies; 60+ messages in thread
From: Dan Williams @ 2021-09-14 23:31 UTC (permalink / raw)
  To: Ben Widawsky
  Cc: linux-cxl, Alison Schofield, Ira Weiny, Jonathan Cameron, Vishal Verma

On Thu, Sep 2, 2021 at 12:50 PM Ben Widawsky <ben.widawsky@intel.com> wrote:
>
> Switches work much in the same way as hostbridges. The primary
> difference is that they are enumerated, and probed via regular PCIe
> mechanisms. A switch has 1 upstream port, and n downstream ports.
> Ultimately a memory device attached to a switch can determine if it's in
> a CXL capable subset of the topology if the switch is CXL capable.
>
> The algorithm introduced enables enumerating switches in a CXL topology.
> It walks up the topology until it finds a root port (which is enumerated
> by the cxl_acpi driver). Once at the top, it walks back down adding all
> downstream ports along the way.
>
> Note that practically speaking there can be at most 3 levels of switches
> with the current 2.0 spec. This is because there is a max interleave of
> 8 defined in the spec. If there is a single hostbridge and only 1 root
> port was CXL capable, you could have 3 levels of x2 switches, making
> the x8 interleave. However, as far as the spec is concerned, there can
> be infinite number of switches since a x1 switch is allowed, and
> future versions of the spec may allow for a larger total interleave.
>
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---
>  drivers/cxl/mem.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-
>  drivers/cxl/pci.c |   8 ---
>  drivers/cxl/pci.h |   8 +++
>  3 files changed, 137 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index aba9a07d519f..dc8ca43d5bfc 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -56,6 +56,133 @@ static bool is_cxl_mem_enabled(struct pci_dev *pdev)
>         return true;
>  }
>
> +/* TODO: dedeuplicate this from drivers/cxl/pci.c? */

No need to carry this debt with the planned port driver reorganization, right?

> +static unsigned long get_component_regs(struct pci_dev *pdev)
> +{
> +       unsigned long component_reg_phys = CXL_RESOURCE_NONE;
> +       u32 regloc_size, regblocks;
> +       int regloc, i;
> +
> +       regloc = cxl_pci_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
> +       if (!regloc) {
> +               dev_err(&pdev->dev, "register location dvsec not found\n");
> +               return component_reg_phys;
> +       }
> +
> +       /* Get the size of the Register Locator DVSEC */
> +       pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
> +       regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
> +
> +       regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
> +       regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
> +
> +       for (i = 0; i < regblocks; i++, regloc += 8) {
> +               u32 reg_lo, reg_hi;
> +               u8 reg_type;
> +               u64 offset;
> +               u8 bar;
> +
> +               pci_read_config_dword(pdev, regloc, &reg_lo);
> +               pci_read_config_dword(pdev, regloc + 4, &reg_hi);
> +
> +               cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,
> +                                         &reg_type);
> +
> +               if (reg_type != CXL_REGLOC_RBI_COMPONENT)
> +                       continue;
> +
> +               component_reg_phys = pci_resource_start(pdev, bar) + offset;
> +       }
> +
> +       return component_reg_phys;
> +}
> +
> +static void enumerate_uport(struct device *dev)
> +{
> +       struct pci_dev *pdev = to_pci_dev(dev);
> +
> +       /*
> +        * Parent's parent should be another uport, since we don't have root
> +        * ports here
> +        */

I don't understand this comment, can you rephrase?

> +       if (dev_WARN_ONCE(dev, !dev->parent->parent, "No grandparent port\n"))
> +               return;

It's not clear that this can only fire in the case of a software bug.
If this might fire at runtime in production it should be dev_warn().

> +
> +       if (!is_cxl_port(dev->parent->parent)) {

Not a fan of multiple de-references... does this grandparent have a better name?

> +               dev_info(dev, "Parent of uport isn't a CXL port (%s)\n",

dev_dbg()?

> +                        dev_name(dev->parent->parent));
> +               return;
> +       }
> +
> +       devm_cxl_add_port(dev, dev, get_component_regs(pdev),
> +                         to_cxl_port(dev->parent));
> +}
> +
> +static void enumerate_dport(struct device *dev)
> +{

Is the argument a dport?

Perhaps this wants a:

struct cxl_dport {
     struct device *dev;
};

...definition to make it clear what argument is being passed?

> +       struct pci_dev *pdev = to_pci_dev(dev);

What about the case where a 'struct acpi_device' is the dport?

> +       u32 port_num, lnkcap;
> +
> +       if (dev_WARN_ONCE(dev, !dev->parent, "No parent port\n"))
> +               return;
> +
> +       if (!is_cxl_port(dev->parent)) {
> +               dev_info(dev, "Uport isn't a CXL port %s\n",
> +                        dev_name(dev->parent));
> +               return;
> +       }
> +
> +       /* TODO: deduplicate from drivers/cxl/acpi.c? */
> +       if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
> +                                 &lnkcap) != PCIBIOS_SUCCESSFUL)
> +               return;
> +       port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
> +
> +       cxl_add_dport(to_cxl_port(dev->parent), dev, port_num,
> +                     get_component_regs(pdev));
> +}
> +

Should the above go straight to a new drivers/cxl/core/pci.c? The
cxl_acpi driver will need this when it is asked to scan for new CXL
ports in the topology.

> +/*
> + * Walk up the topology until we get to the root port (ie. parent is a
> + * cxl port). From there walk back down adding the additional ports. If the
> + * parent isn't a PCIe switch (upstream or downstream port), the downstream
> + * endpoint(s) cannot be CXL enabled.
> + *
> + * XXX: It's possible that cxl_acpi hasn't yet enumerated the root ports, and
> + * so that will rescan the CXL bus, thus coming back here.
> + */
> +static void enumerate_switches(struct device *dev)
> +{
> +       struct pci_dev *pdev;
> +       int type;
> +
> +       if (unlikely(!dev))
> +               return;
> +
> +       if (unlikely(!dev_is_pci(dev)))
> +               return;
> +
> +       pdev = to_pci_dev(dev);
> +
> +       if (unlikely(!pci_is_pcie(pdev)))
> +               return;

unlikely() is a micro-optimization only after demonstrating
performance harm from cache pollution, not to document things that
generally won't happen in slow paths.


> +
> +       if (!is_cxl_mem_enabled(pdev))
> +               return;
> +
> +       type = pci_pcie_type(pdev);
> +
> +       if (type != PCI_EXP_TYPE_UPSTREAM && type != PCI_EXP_TYPE_DOWNSTREAM)
> +               return;
> +
> +       enumerate_switches(dev->parent);
> +
> +       if (type == PCI_EXP_TYPE_UPSTREAM)
> +               enumerate_uport(dev);
> +       if (type == PCI_EXP_TYPE_DOWNSTREAM)
> +               enumerate_dport(dev);
> +}
> +
>  static int cxl_mem_probe(struct device *dev)
>  {
>         struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> @@ -68,7 +195,8 @@ static int cxl_mem_probe(struct device *dev)
>         if (!is_cxl_mem_enabled(pdev))
>                 return -ENODEV;
>
> -       /* TODO: if parent is a switch, this will fail. */
> +       enumerate_switches(dev->parent);
> +
>         port_dev = bus_find_device(&cxl_bus_type, NULL, pdev_parent, port_match);
>         if (!port_dev)
>                 return -ENODEV;
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 258190febb5a..e338f2f759d0 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -400,14 +400,6 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
>         return 0;
>  }
>
> -static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
> -                                     u8 *bar, u64 *offset, u8 *reg_type)
> -{
> -       *offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
> -       *bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
> -       *reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
> -}
> -
>  /**
>   * cxl_pci_setup_regs() - Setup necessary MMIO.
>   * @cxlm: The CXL memory device to communicate with.
> diff --git a/drivers/cxl/pci.h b/drivers/cxl/pci.h
> index d6b9978d05b0..8250d487e39d 100644
> --- a/drivers/cxl/pci.h
> +++ b/drivers/cxl/pci.h
> @@ -34,4 +34,12 @@
>
>  int cxl_pci_dvsec(struct pci_dev *pdev, int dvsec);
>
> +static inline void cxl_decode_register_block(u32 reg_lo, u32 reg_hi, u8 *bar,
> +                                            u64 *offset, u8 *reg_type)
> +{
> +       *offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
> +       *bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
> +       *reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
> +}
> +
>  #endif /* __CXL_PCI_H__ */
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 60+ messages in thread

end of thread, other threads:[~2021-09-14 23:31 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-02 19:50 [PATCH 00/13] Enumerate midlevel and endpoint decoders Ben Widawsky
2021-09-02 19:50 ` [PATCH 01/13] Documentation/cxl: Add bus internal docs Ben Widawsky
2021-09-03 14:05   ` Jonathan Cameron
2021-09-10 18:20     ` Dan Williams
2021-09-02 19:50 ` [PATCH 02/13] cxl/core/bus: Add kernel docs for decoder ops Ben Widawsky
2021-09-03 14:17   ` Jonathan Cameron
2021-09-10 18:51   ` Dan Williams
2021-09-11 17:25     ` Ben Widawsky
2021-09-02 19:50 ` [PATCH 03/13] cxl/core: Ignore interleave when adding decoders Ben Widawsky
2021-09-03 14:25   ` Jonathan Cameron
2021-09-10 19:00     ` Dan Williams
2021-09-11 17:30       ` Ben Widawsky
2021-09-02 19:50 ` [PATCH 04/13] cxl: Introduce endpoint decoders Ben Widawsky
2021-09-03 14:35   ` Jonathan Cameron
2021-09-13 16:19     ` Ben Widawsky
2021-09-10 19:19   ` Dan Williams
2021-09-13 16:11     ` Ben Widawsky
2021-09-13 22:07       ` Dan Williams
2021-09-13 23:19         ` Ben Widawsky
2021-09-14 21:16           ` Dan Williams
2021-09-02 19:50 ` [PATCH 05/13] cxl/pci: Disambiguate cxl_pci further from cxl_mem Ben Widawsky
2021-09-03 14:45   ` Jonathan Cameron
2021-09-10 19:27   ` Dan Williams
2021-09-02 19:50 ` [PATCH 06/13] cxl/mem: Introduce cxl_mem driver Ben Widawsky
2021-09-03 14:52   ` Jonathan Cameron
2021-09-10 21:32   ` Dan Williams
2021-09-13 16:46     ` Ben Widawsky
2021-09-13 19:37       ` Dan Williams
2021-09-02 19:50 ` [PATCH 07/13] cxl/memdev: Determine CXL.mem capability Ben Widawsky
2021-09-03 15:21   ` Jonathan Cameron
2021-09-13 19:01     ` Ben Widawsky
2021-09-10 21:59   ` Dan Williams
2021-09-13 22:10     ` Ben Widawsky
2021-09-14 22:42       ` Dan Williams
2021-09-14 22:55         ` Ben Widawsky
2021-09-02 19:50 ` [PATCH 08/13] cxl/mem: Add memdev as a port Ben Widawsky
2021-09-03 15:31   ` Jonathan Cameron
2021-09-10 23:09   ` Dan Williams
2021-09-02 19:50 ` [PATCH 09/13] cxl/pci: Retain map information in cxl_mem_probe Ben Widawsky
2021-09-10 23:12   ` Dan Williams
2021-09-10 23:45     ` Dan Williams
2021-09-02 19:50 ` [PATCH 10/13] cxl/core: Map component registers for ports Ben Widawsky
2021-09-02 22:41   ` Ben Widawsky
2021-09-02 22:42     ` Ben Widawsky
2021-09-03 16:14   ` Jonathan Cameron
2021-09-10 23:52     ` Dan Williams
2021-09-13  8:29       ` Jonathan Cameron
2021-09-10 23:44   ` Dan Williams
2021-09-02 19:50 ` [PATCH 11/13] cxl/core: Convert decoder range to resource Ben Widawsky
2021-09-03 16:16   ` Jonathan Cameron
2021-09-11  0:59   ` Dan Williams
2021-09-02 19:50 ` [PATCH 12/13] cxl/core/bus: Enumerate all HDM decoders Ben Widawsky
2021-09-03 17:43   ` Jonathan Cameron
2021-09-11  1:37     ` Dan Williams
2021-09-11  1:13   ` Dan Williams
2021-09-02 19:50 ` [PATCH 13/13] cxl/mem: Enumerate switch decoders Ben Widawsky
2021-09-03 17:56   ` Jonathan Cameron
2021-09-13 22:12     ` Ben Widawsky
2021-09-14 23:31   ` Dan Williams
2021-09-10 18:15 ` [PATCH 00/13] Enumerate midlevel and endpoint decoders Dan Williams

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).