[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