Skip to content

Commit e29a899

Browse files
committed
cxl/region: Fix region reference target accounting
Dan reports: The error handling in cxl_port_attach_region() looks like it might have a similar bug. The cxl_rr->nr_targets++; might want a --. That function is more complicated. Indeed cxl_rr->nr_targets leaks when cxl_rr_ep_add() fails, but that flow is not clear. Fix the bug and the clarity by separating the 'new' region-reference case from the 'extend' region-reference case. This also moves the host-physical-address (HPA) validation, that the HPA of a new region being accounted to the port is greater than the HPA of all other regions associated with the port, to alloc_region_ref(). Introduce @nr_targets_inc to track when the error exit path needs to clean up cxl_rr->nr_targets. Fixes: 384e624 ("cxl/region: Attach endpoint decoders") Reported-by: Dan Carpenter <[email protected]> Reviewed-by: Ira Weiny <[email protected]> Link: http://lore.kernel.org/r/165939482134.252363.1915691883146696327.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Dan Williams <[email protected]>
1 parent 69c9961 commit e29a899

File tree

1 file changed

+43
-28
lines changed

1 file changed

+43
-28
lines changed

drivers/cxl/core/region.c

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -648,12 +648,26 @@ static struct cxl_decoder *cxl_region_find_decoder(struct cxl_port *port,
648648
static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port,
649649
struct cxl_region *cxlr)
650650
{
651-
struct cxl_region_ref *cxl_rr;
651+
struct cxl_region_params *p = &cxlr->params;
652+
struct cxl_region_ref *cxl_rr, *iter;
653+
unsigned long index;
652654
int rc;
653655

656+
xa_for_each(&port->regions, index, iter) {
657+
struct cxl_region_params *ip = &iter->region->params;
658+
659+
if (ip->res->start > p->res->start) {
660+
dev_dbg(&cxlr->dev,
661+
"%s: HPA order violation %s:%pr vs %pr\n",
662+
dev_name(&port->dev),
663+
dev_name(&iter->region->dev), ip->res, p->res);
664+
return ERR_PTR(-EBUSY);
665+
}
666+
}
667+
654668
cxl_rr = kzalloc(sizeof(*cxl_rr), GFP_KERNEL);
655669
if (!cxl_rr)
656-
return NULL;
670+
return ERR_PTR(-ENOMEM);
657671
cxl_rr->port = port;
658672
cxl_rr->region = cxlr;
659673
cxl_rr->nr_targets = 1;
@@ -665,7 +679,7 @@ static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port,
665679
"%s: failed to track region reference: %d\n",
666680
dev_name(&port->dev), rc);
667681
kfree(cxl_rr);
668-
return NULL;
682+
return ERR_PTR(rc);
669683
}
670684

671685
return cxl_rr;
@@ -740,33 +754,25 @@ static int cxl_port_attach_region(struct cxl_port *port,
740754
{
741755
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
742756
struct cxl_ep *ep = cxl_ep_load(port, cxlmd);
743-
struct cxl_region_ref *cxl_rr = NULL, *iter;
744-
struct cxl_region_params *p = &cxlr->params;
745-
struct cxl_decoder *cxld = NULL;
757+
struct cxl_region_ref *cxl_rr;
758+
bool nr_targets_inc = false;
759+
struct cxl_decoder *cxld;
746760
unsigned long index;
747761
int rc = -EBUSY;
748762

749763
lockdep_assert_held_write(&cxl_region_rwsem);
750764

751-
xa_for_each(&port->regions, index, iter) {
752-
struct cxl_region_params *ip = &iter->region->params;
753-
754-
if (iter->region == cxlr)
755-
cxl_rr = iter;
756-
if (ip->res->start > p->res->start) {
757-
dev_dbg(&cxlr->dev,
758-
"%s: HPA order violation %s:%pr vs %pr\n",
759-
dev_name(&port->dev),
760-
dev_name(&iter->region->dev), ip->res, p->res);
761-
return -EBUSY;
762-
}
763-
}
764-
765+
cxl_rr = cxl_rr_load(port, cxlr);
765766
if (cxl_rr) {
766767
struct cxl_ep *ep_iter;
767768
int found = 0;
768769

769-
cxld = cxl_rr->decoder;
770+
/*
771+
* Walk the existing endpoints that have been attached to
772+
* @cxlr at @port and see if they share the same 'next' port
773+
* in the downstream direction. I.e. endpoints that share common
774+
* upstream switch.
775+
*/
770776
xa_for_each(&cxl_rr->endpoints, index, ep_iter) {
771777
if (ep_iter == ep)
772778
continue;
@@ -777,22 +783,29 @@ static int cxl_port_attach_region(struct cxl_port *port,
777783
}
778784

779785
/*
780-
* If this is a new target or if this port is direct connected
781-
* to this endpoint then add to the target count.
786+
* New target port, or @port is an endpoint port that always
787+
* accounts its own local decode as a target.
782788
*/
783-
if (!found || !ep->next)
789+
if (!found || !ep->next) {
784790
cxl_rr->nr_targets++;
791+
nr_targets_inc = true;
792+
}
793+
794+
/*
795+
* The decoder for @cxlr was allocated when the region was first
796+
* attached to @port.
797+
*/
798+
cxld = cxl_rr->decoder;
785799
} else {
786800
cxl_rr = alloc_region_ref(port, cxlr);
787-
if (!cxl_rr) {
801+
if (IS_ERR(cxl_rr)) {
788802
dev_dbg(&cxlr->dev,
789803
"%s: failed to allocate region reference\n",
790804
dev_name(&port->dev));
791-
return -ENOMEM;
805+
return PTR_ERR(cxl_rr);
792806
}
793-
}
807+
nr_targets_inc = true;
794808

795-
if (!cxld) {
796809
if (port == cxled_to_port(cxled))
797810
cxld = &cxled->cxld;
798811
else
@@ -835,6 +848,8 @@ static int cxl_port_attach_region(struct cxl_port *port,
835848

836849
return 0;
837850
out_erase:
851+
if (nr_targets_inc)
852+
cxl_rr->nr_targets--;
838853
if (cxl_rr->nr_eps == 0)
839854
free_region_ref(cxl_rr);
840855
return rc;

0 commit comments

Comments
 (0)