Re: [PATCH v5 2/5] cxl/pci: Add BI topology enable/disable

Davidlohr Bueso <[email protected]>
Newsgroups org.kernel.vger.linux-cxl,dev.linux.lists.sashiko-reviews
Message-ID <20260714020116.gehv2wfqrlecu5bc@offworld>
On Mon, 15 Jun 2026, [email protected] wrote:

>Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
>- [High] Mixing `goto` error handling with `__free()` scope-based cleanup declarations.

Trivial, fixed for v6. See below.

>- [High] Missing explicit commit for the Endpoint BI Decoder Control Register.

False positive, this commit does not apply to endpoints.

>- [High] Root port BI enablement logic is unreachable and contradicts the commit message.

False positive.

Sashiko read !is_cxl_root(port_iter) as "the RP is skipped," but the RP sits a
level below the platform root.

>- [High] Missing reference counting (`nr_bi`) for shared downstream ports in cascaded switch topologies.

I am avoiding cascaded switch topologies (PBR/HBR), nor does this touch at all wrt
BI in the latest spec.

See: https://lore.kernel.org/all/20260608000226.jb2ploxc5nkjestk@offworld/

>--
>
>commit 31994c2bf9d14374bacdb07d505a19b18a761e35
>Author: Davidlohr Bueso <[email protected]>
>
>cxl/pci: Add BI topology enable/disable
>
>This commit implements cxl_bi_setup() to walk the CXL port topology and enable
>BI flows on the device and every component in the path. It also implements a
>teardown counterpart, cxl_bi_dealloc().
>
>> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
>> index d1f487b3d809a..b1b0f3eab90e8 100644
>> --- a/drivers/cxl/core/pci.c
>> +++ b/drivers/cxl/core/pci.c
>
>[ ... ]
>
>> +static int __cxl_bi_ctrl_dport(struct cxl_dport *dport, bool enable)
>> +{
>> +	struct pci_dev *pdev = to_pci_dev(dport->dport_dev);
>> +	void __iomem *bi = dport->regs.bi_decoder;
>> +	struct cxl_port *port = dport->port;
>> +	u32 ctrl, value;
>> +	int rc;
>
>[ ... ]
>
>> +	case PCI_EXP_TYPE_DOWNSTREAM:
>> +		if (enable) {
>> +			value = ctrl & ~CXL_BI_DECODER_CTRL_BI_FW;
>> +			value |= CXL_BI_DECODER_CTRL_BI_ENABLE;
>> +		} else {
>> +			if (!FIELD_GET(CXL_BI_DECODER_CTRL_BI_ENABLE, ctrl))
>> +				return 0;
>> +			value = ctrl & ~(CXL_BI_DECODER_CTRL_BI_FW |
>> +					 CXL_BI_DECODER_CTRL_BI_ENABLE);
>> +		}
>
>[Severity: High]
>In cascaded switch topologies, can this unconditionally clear the
>CXL_BI_DECODER_CTRL_BI_ENABLE bit while other endpoints below the switch
>are still actively using it?
>
>Unlike the root port case which uses dport->nr_bi for reference counting,
>downstream ports appear to clear this bit as soon as the first endpoint
>unbinds. This might break BI routing for other active endpoints on a shared
>downstream port.
>
>Does this need reference counting similar to the root port?
>
>[ ... ]
>
>> +static int __cxl_bi_ctrl_endpoint(struct cxl_dev_state *cxlds, bool enable)
>> +{
>
>[ ... ]
>
>> +	writel(val, bi + CXL_BI_DECODER_CTRL_OFFSET);
>> +	cxlds->bi = enable;
>> +
>> +	dev_dbg(cxlds->dev, "BI requests %s\n",
>> +		str_enabled_disabled(enable));
>> +
>> +	return 0;
>> +}
>
>[Severity: High]
>Is this missing an explicit commit for the Endpoint BI Decoder Control
>Register?
>
>The CXL 4.0 specification requires an explicit commit after changing
>BI_ENABLE for any BI Decoder via CXL_BI_DECODER_CAPS_EXPLICIT_COMMIT_REQ.
>
>While __cxl_bi_ctrl_dport() correctly calls __cxl_bi_commit_decoder() after
>updating the downstream ports, __cxl_bi_ctrl_endpoint() omits this step.
>
>Could this cause the endpoint device to ignore the BI enablement state and
>silently fail to enable BI flows?
>
>[ ... ]
>
>> +int cxl_bi_setup(struct cxl_dev_state *cxlds)
>> +{
>> +	struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
>> +	struct cxl_dport *dport_iter, *dport, *failed;
>> +	struct cxl_port *port_iter;
>> +	struct pci_dev *pdev;
>> +	int rc;
>> +
>> +	if (!dev_is_pci(cxlds->dev))
>> +		return 0;
>> +
>> +	pdev = to_pci_dev(cxlds->dev);
>> +	struct cxl_port *port __free(put_cxl_port) =
>> +		cxl_pci_find_port(pdev, &dport);
>> +
>> +	if (!port)
>> +		return -EINVAL;
>
>[ ... ]
>
>> +	port_iter = port;
>> +	dport_iter = dport;
>> +	while (!is_cxl_root(port_iter)) {
>> +		rc = cxl_bi_ctrl_dport_enable(dport_iter);
>> +		if (rc)
>> +			goto err_rollback;
>
>[Severity: High]
>The commit message states this will write BI Enable on each dport's BI
>Decoder Control register. However, doesn't this loop terminate before
>processing the root port?
>
>Since the loop condition is !is_cxl_root(port_iter), when port_iter reaches
>the CXL root, the loop exits and skips configuring the root port entirely.
>
>Additionally, if the loop did reach the PCI_EXP_TYPE_ROOT_PORT code inside
>__cxl_bi_ctrl_dport(), that code actually clears the enable bit:
>
>__cxl_bi_ctrl_dport() {
>    ...
>    case PCI_EXP_TYPE_ROOT_PORT:
>        ...
>        value &= ~CXL_BI_DECODER_CTRL_BI_ENABLE;
>}
>
>Should this loop include the root port, and should the root port logic actually
>set the enable bit rather than clearing it?
>
>[ ... ]
>
>> +	/* finally, enable BI on the device */
>> +	rc = cxl_bi_ctrl_endpoint_enable(cxlds);
>> +	if (rc)
>> +		goto err_rollback;
>> +
>> +	return devm_add_action_or_reset(&cxlds->cxlmd->dev,
>> +					cxl_bi_dealloc, cxlds);
>> +
>> +err_rollback:
>> +	/*
>> +	 * Undo all dports enabled so far, including the failed one.
>> +	 */
>> +	failed = dport_iter;
>> +	dport_iter = dport;
>> +	port_iter = port;
>> +	while (!is_cxl_root(port_iter)) {
>> +		cxl_bi_ctrl_dport_disable(dport_iter);
>> +		if (dport_iter == failed)
>> +			break;
>> +		dport_iter = port_iter->parent_dport;
>> +		port_iter = dport_iter->port;
>> +	}
>> +	return rc;
>> +}
>
>[Severity: High]
>Is it safe to mix goto-based error handling with scope-based cleanup in
>this function?
>
>This function declares struct cxl_port *port __free(put_cxl_port) at the
>top, but also uses a goto err_rollback pattern for failure paths.
>
>Mixing goto labels with __free() declarations in the same function creates
>confusing ownership semantics and violates subsystem cleanup guidelines.
>
>Should this be fully converted to scope-based cleanup, or avoid using
>__free() here?

Sure. I went with this this change/unwind consolidation, fyi:

diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index ed54268e9ec7..dc700f7c75c0 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -1247,10 +1247,52 @@ static void cxl_bi_dealloc(void *data)
  	}
  }
  
+/*
+ * Enable BI on every dport in the path, then on the device itself.
+ * On failure, unwind only the dports that fully enabled.
+ */
+static int cxl_bi_enable_path(struct cxl_dev_state *cxlds,
+			      struct cxl_port *port, struct cxl_dport *dport)
+{
+	struct cxl_dport *dport_iter, *failed;
+	struct cxl_port *port_iter;
+	int rc;
+
+	port_iter = port;
+	dport_iter = dport;
+	while (!is_cxl_root(port_iter)) {
+		rc = cxl_bi_ctrl_dport_enable(dport_iter);
+		if (rc)
+			goto err_rollback;
+
+		dport_iter = port_iter->parent_dport;
+		port_iter = dport_iter->port;
+	}
+
+	/* finally, enable BI on the device */
+	rc = cxl_bi_ctrl_endpoint_enable(cxlds);
+	if (rc)
+		goto err_rollback;
+
+	return 0;
+
+err_rollback:
+	/* Undo dports that fully enabled; skip the failed one. */
+	failed = dport_iter;
+	dport_iter = dport;
+	port_iter = port;
+	while (!is_cxl_root(port_iter) && dport_iter != failed) {
+		cxl_bi_ctrl_dport_disable(dport_iter);
+		dport_iter = port_iter->parent_dport;
+		port_iter = dport_iter->port;
+	}
+	return rc;
+}
+
  int cxl_bi_setup(struct cxl_dev_state *cxlds)
  {
  	struct cxl_port *endpoint = cxlds->cxlmd->endpoint;
-	struct cxl_dport *dport_iter, *dport, *failed;
+	struct cxl_dport *dport_iter, *dport;
  	struct cxl_port *port_iter;
  	struct pci_dev *pdev;
  	int rc;
@@ -1293,35 +1335,11 @@ int cxl_bi_setup(struct cxl_dev_state *cxlds)
  		port_iter = dport_iter->port;
  	}
  
-	port_iter = port;
-	dport_iter = dport;
-	while (!is_cxl_root(port_iter)) {
-		rc = cxl_bi_ctrl_dport_enable(dport_iter);
-		if (rc)
-			goto err_rollback;
-
-		dport_iter = port_iter->parent_dport;
-		port_iter = dport_iter->port;
-	}
-
-	/* finally, enable BI on the device */
-	rc = cxl_bi_ctrl_endpoint_enable(cxlds);
+	rc = cxl_bi_enable_path(cxlds, port, dport);
  	if (rc)
-		goto err_rollback;
+		return rc;
  
  	return devm_add_action_or_reset(&cxlds->cxlmd->dev,
  					cxl_bi_dealloc, cxlds);
-
-err_rollback:
-	/* Undo dports that fully enabled; skip the failed one. */
-	failed = dport_iter;
-	dport_iter = dport;
-	port_iter = port;
-	while (!is_cxl_root(port_iter) && dport_iter != failed) {
-		cxl_bi_ctrl_dport_disable(dport_iter);
-		dport_iter = port_iter->parent_dport;
-		port_iter = dport_iter->port;
-	}
-	return rc;
  }
  EXPORT_SYMBOL_NS_GPL(cxl_bi_setup, "CXL");
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.