[PATCH v2 3/3] cxl/hdm: Restore commit_end when decoder enumeration fails

Alison Schofield <[email protected]>
Newsgroups org.kernel.vger.linux-cxl
Message-ID <563095ff87e66b72651c15248a1a6dd6f5ddc3b5.1784150131.git.alison.schofield@intel.com>
commit_end tracks the highest committed decoder on a port. It gets
advanced before decoder enumeration is complete, so a later failure
leaves it pointing at a decoder that was never added. The next probe
then rejects decoder0 as out of order and enumeration fails.

Restore commit_end to the none-committed baseline on the enumeration
failure path so a subsequent probe can enumerate decoders cleanly. The
successful path never touches commit_end, so no transient count is
exposed to concurrent readers.

Protect the commit_end advance in the DVSEC emulation path against
concurrent readers, matching the register-programmed path.

Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Fixes: b777e9bec960 ("cxl/hdm: Emulate HDM decoder from DVSEC range registers")
Signed-off-by: Alison Schofield <[email protected]>
---
 drivers/cxl/core/hdm.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 51ab154a73fe..850f509432aa 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -955,7 +955,8 @@ static int cxl_setup_hdm_decoder_from_dvsec(
 	 * change the range registers at run time.
 	 */
 	cxld->flags |= CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK;
-	port->commit_end = cxld->id;
+	scoped_guard(rwsem_write, &cxl_rwsem.region)
+		port->commit_end = cxld->id;
 
 	rc = devm_cxl_dpa_reserve(cxled, *dpa_base, len, 0);
 	if (rc) {
@@ -1181,13 +1182,13 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
 {
 	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
 	struct cxl_port *port = cxlhdm->port;
-	int i;
 	u64 dpa_base = 0;
+	int i, rc;
 
 	cxl_settle_decoders(cxlhdm);
 
 	for (i = 0; i < cxlhdm->decoder_count; i++) {
-		int rc, target_count = cxlhdm->target_count;
+		int target_count = cxlhdm->target_count;
 		struct cxl_decoder *cxld;
 
 		if (is_cxl_endpoint(port)) {
@@ -1198,7 +1199,8 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
 				dev_warn(&port->dev,
 					 "Failed to allocate decoder%d.%d\n",
 					 port->id, i);
-				return PTR_ERR(cxled);
+				rc = PTR_ERR(cxled);
+				goto err;
 			}
 			cxld = &cxled->cxld;
 		} else {
@@ -1209,7 +1211,8 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
 				dev_warn(&port->dev,
 					 "Failed to allocate decoder%d.%d\n",
 					 port->id, i);
-				return PTR_ERR(cxlsd);
+				rc = PTR_ERR(cxlsd);
+				goto err;
 			}
 			cxld = &cxlsd->cxld;
 		}
@@ -1220,17 +1223,23 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
 				 "Failed to initialize decoder%d.%d\n",
 				 port->id, i);
 			put_device(&cxld->dev);
-			return rc;
+			goto err;
 		}
 		rc = add_hdm_decoder(port, cxld);
 		if (rc) {
 			dev_warn(&port->dev,
 				 "Failed to add decoder%d.%d\n", port->id, i);
-			return rc;
+			goto err;
 		}
 	}
 
 	return 0;
+
+err:
+	/* Reset to the none committed baseline for a fresh enumeration */
+	scoped_guard(rwsem_write, &cxl_rwsem.region)
+		port->commit_end = -1;
+	return rc;
 }
 
 /**
-- 
2.37.3
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.