cxl/port: Prevent out-of-order decoder allocation

With the recent change to allow out-of-order decoder de-commit it
highlights a need to strengthen the in-order decoder commit guarantees.
As it stands match_free_decoder() ensures that if 2 regions are racing
decoder allocations the one that wins the race will get the lower id
decoder, but that still leaves the race to *commit* the decoder.

Rather than have this complicated case of "reserved in-order, but may
still commit out-of-order", just arrange for the reservation order to
match the commit-order. In other words, prevent subsequent allocations
until the last reservation is committed.

This precludes overlapping region creation events and requires the
previous regionN to either move forward to the decoder commit stage or
drop its reservation before regionN+1 can move forward. That is,
provided that regionN and regionN+1 decode through the same switch port.

As a side effect this allows match_free_decoder() to drop its dependency
on needing write access to the device_find_child() @data parameter [1].

Reported-by: Zijun Hu <quic_zijuhu@quicinc.com>
Closes: http://lore.kernel.org/20240905-const_dfc_prepare-v4-0-4180e1d5a244@quicinc.com
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964783668.81806.14962699553881333486.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
This commit is contained in:
Dan Williams 2024-10-22 18:43:57 -07:00 committed by Ira Weiny
parent 101c268bd2
commit 105b6235ad

View File

@ -778,26 +778,50 @@ out:
return rc;
}
static int check_commit_order(struct device *dev, const void *data)
{
struct cxl_decoder *cxld = to_cxl_decoder(dev);
/*
* if port->commit_end is not the only free decoder, then out of
* order shutdown has occurred, block further allocations until
* that is resolved
*/
if (((cxld->flags & CXL_DECODER_F_ENABLE) == 0))
return -EBUSY;
return 0;
}
static int match_free_decoder(struct device *dev, void *data)
{
struct cxl_port *port = to_cxl_port(dev->parent);
struct cxl_decoder *cxld;
int *id = data;
int rc;
if (!is_switch_decoder(dev))
return 0;
cxld = to_cxl_decoder(dev);
/* enforce ordered allocation */
if (cxld->id != *id)
if (cxld->id != port->commit_end + 1)
return 0;
if (!cxld->region)
return 1;
if (cxld->region) {
dev_dbg(dev->parent,
"next decoder to commit (%s) is already reserved (%s)\n",
dev_name(dev), dev_name(&cxld->region->dev));
return 0;
}
(*id)++;
return 0;
rc = device_for_each_child_reverse_from(dev->parent, dev, NULL,
check_commit_order);
if (rc) {
dev_dbg(dev->parent,
"unable to allocate %s due to out of order shutdown\n",
dev_name(dev));
return 0;
}
return 1;
}
static int match_auto_decoder(struct device *dev, void *data)
@ -824,7 +848,6 @@ cxl_region_find_decoder(struct cxl_port *port,
struct cxl_region *cxlr)
{
struct device *dev;
int id = 0;
if (port == cxled_to_port(cxled))
return &cxled->cxld;
@ -833,7 +856,7 @@ cxl_region_find_decoder(struct cxl_port *port,
dev = device_find_child(&port->dev, &cxlr->params,
match_auto_decoder);
else
dev = device_find_child(&port->dev, &id, match_free_decoder);
dev = device_find_child(&port->dev, NULL, match_free_decoder);
if (!dev)
return NULL;
/*