[PATCH v3 3/3] cxl/hdm: Restore commit_end when decoder enumeration fails
Alison Schofield <[email protected]>
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <d75080ecb9fad922623781e9e354baab05842c2a.1784322343.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.
Reset commit_end to the none committed baseline once the decoders are
torn down, so a subsequent probe rebuilds it from scratch. Register the
reset before the decoders are added so that on unwind it runs after
every decoder has been unregistered. This prevents commit_end from
dropping below the highest committed decoder still registered and
exposing an inconsistent value to a concurrent reader.
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, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 4aaadb842d90..6dc6e53c28c6 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) {
@@ -1183,18 +1184,34 @@ static void cxl_settle_decoders(struct cxl_hdm *cxlhdm)
* @cxlhdm: Structure to populate with HDM capabilities
* @info: cached DVSEC range register info
*/
+static void cxl_reset_commit_end(void *data)
+{
+ struct cxl_port *port = data;
+
+ guard(rwsem_write)(&cxl_rwsem.region);
+ port->commit_end = -1;
+}
+
static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info)
{
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);
+ /*
+ * Reset commit_end after all decoders have been torn down so a
+ * subsequent probe rebuilds it from scratch.
+ */
+ rc = devm_add_action_or_reset(&port->dev, cxl_reset_commit_end, port);
+ if (rc)
+ return rc;
+
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)) {
--
2.37.3