Skip to content

Commit 27b3f8d

Browse files
committed
cxl/region: Program target lists
Once the region's interleave geometry (ways, granularity, size) is established and all the endpoint decoder targets are assigned, the next phase is to program all the intermediate decoders. Specifically, each CXL switch in the path between the endpoint and its CXL host-bridge (including the logical switch internal to the host-bridge) needs to have its decoders programmed and the target list order assigned. The difficulty in this implementation lies in determining which endpoint decoder ordering combinations are valid. Consider the cxl_test case of 2 host bridges, each of those host-bridges attached to 2 switches, and each of those switches attached to 2 endpoints for a potential 8-way interleave. The x2 interleave at the host-bridge level requires that all even numbered endpoint decoder positions be located on the "left" hand side of the topology tree, and the odd numbered positions on the other. The endpoints that are peers on the same switch need to have a position that can be routed with a dedicated address bit per-endpoint. See check_last_peer() for the details. Reviewed-by: Jonathan Cameron <[email protected]> Link: https://lore.kernel.org/r/165784337827.1758207.132121746122685208.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Dan Williams <[email protected]>
1 parent 384e624 commit 27b3f8d

File tree

4 files changed

+259
-11
lines changed

4 files changed

+259
-11
lines changed

drivers/cxl/core/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@ resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled);
4242
extern struct rw_semaphore cxl_dpa_rwsem;
4343

4444
bool is_switch_decoder(struct device *dev);
45+
struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev);
4546
static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port,
4647
struct cxl_memdev *cxlmd)
4748
{
49+
if (!port)
50+
return NULL;
51+
4852
return xa_load(&port->endpoints, (unsigned long)&cxlmd->dev);
4953
}
5054

drivers/cxl/core/port.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ static ssize_t emit_target_list(struct cxl_switch_decoder *cxlsd, char *buf)
146146
return offset;
147147
}
148148

149-
static struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev);
150-
151149
static ssize_t target_list_show(struct device *dev,
152150
struct device_attribute *attr, char *buf)
153151
{
@@ -472,7 +470,7 @@ struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev)
472470
}
473471
EXPORT_SYMBOL_NS_GPL(to_cxl_endpoint_decoder, CXL);
474472

475-
static struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev)
473+
struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev)
476474
{
477475
if (dev_WARN_ONCE(dev, !is_switch_decoder(dev),
478476
"not a cxl_switch_decoder device\n"))

drivers/cxl/core/region.c

Lines changed: 252 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port,
491491
return NULL;
492492
cxl_rr->port = port;
493493
cxl_rr->region = cxlr;
494+
cxl_rr->nr_targets = 1;
494495
xa_init(&cxl_rr->endpoints);
495496

496497
rc = xa_insert(&port->regions, (unsigned long)cxlr, cxl_rr, GFP_KERNEL);
@@ -531,10 +532,12 @@ static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr,
531532
struct cxl_decoder *cxld = cxl_rr->decoder;
532533
struct cxl_ep *ep = cxl_ep_load(port, cxled_to_memdev(cxled));
533534

534-
rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep,
535-
GFP_KERNEL);
536-
if (rc)
537-
return rc;
535+
if (ep) {
536+
rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep,
537+
GFP_KERNEL);
538+
if (rc)
539+
return rc;
540+
}
538541
cxl_rr->nr_eps++;
539542

540543
if (!cxld->region) {
@@ -655,6 +658,16 @@ static int cxl_port_attach_region(struct cxl_port *port,
655658
goto out_erase;
656659
}
657660

661+
dev_dbg(&cxlr->dev,
662+
"%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n",
663+
dev_name(port->uport), dev_name(&port->dev),
664+
dev_name(&cxld->dev), dev_name(&cxlmd->dev),
665+
dev_name(&cxled->cxld.dev), pos,
666+
ep ? ep->next ? dev_name(ep->next->uport) :
667+
dev_name(&cxlmd->dev) :
668+
"none",
669+
cxl_rr->nr_eps, cxl_rr->nr_targets);
670+
658671
return 0;
659672
out_erase:
660673
if (cxl_rr->nr_eps == 0)
@@ -673,15 +686,22 @@ static void cxl_port_detach_region(struct cxl_port *port,
673686
struct cxl_endpoint_decoder *cxled)
674687
{
675688
struct cxl_region_ref *cxl_rr;
676-
struct cxl_ep *ep;
689+
struct cxl_ep *ep = NULL;
677690

678691
lockdep_assert_held_write(&cxl_region_rwsem);
679692

680693
cxl_rr = cxl_rr_load(port, cxlr);
681694
if (!cxl_rr)
682695
return;
683696

684-
ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled);
697+
/*
698+
* Endpoint ports do not carry cxl_ep references, and they
699+
* never target more than one endpoint by definition
700+
*/
701+
if (cxl_rr->decoder == &cxled->cxld)
702+
cxl_rr->nr_eps--;
703+
else
704+
ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled);
685705
if (ep) {
686706
struct cxl_ep *ep_iter;
687707
unsigned long index;
@@ -702,6 +722,224 @@ static void cxl_port_detach_region(struct cxl_port *port,
702722
free_region_ref(cxl_rr);
703723
}
704724

725+
static int check_last_peer(struct cxl_endpoint_decoder *cxled,
726+
struct cxl_ep *ep, struct cxl_region_ref *cxl_rr,
727+
int distance)
728+
{
729+
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
730+
struct cxl_region *cxlr = cxl_rr->region;
731+
struct cxl_region_params *p = &cxlr->params;
732+
struct cxl_endpoint_decoder *cxled_peer;
733+
struct cxl_port *port = cxl_rr->port;
734+
struct cxl_memdev *cxlmd_peer;
735+
struct cxl_ep *ep_peer;
736+
int pos = cxled->pos;
737+
738+
/*
739+
* If this position wants to share a dport with the last endpoint mapped
740+
* then that endpoint, at index 'position - distance', must also be
741+
* mapped by this dport.
742+
*/
743+
if (pos < distance) {
744+
dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n",
745+
dev_name(port->uport), dev_name(&port->dev),
746+
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
747+
return -ENXIO;
748+
}
749+
cxled_peer = p->targets[pos - distance];
750+
cxlmd_peer = cxled_to_memdev(cxled_peer);
751+
ep_peer = cxl_ep_load(port, cxlmd_peer);
752+
if (ep->dport != ep_peer->dport) {
753+
dev_dbg(&cxlr->dev,
754+
"%s:%s: %s:%s pos %d mismatched peer %s:%s\n",
755+
dev_name(port->uport), dev_name(&port->dev),
756+
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos,
757+
dev_name(&cxlmd_peer->dev),
758+
dev_name(&cxled_peer->cxld.dev));
759+
return -ENXIO;
760+
}
761+
762+
return 0;
763+
}
764+
765+
static int cxl_port_setup_targets(struct cxl_port *port,
766+
struct cxl_region *cxlr,
767+
struct cxl_endpoint_decoder *cxled)
768+
{
769+
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
770+
int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos;
771+
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
772+
struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
773+
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
774+
struct cxl_ep *ep = cxl_ep_load(port, cxlmd);
775+
struct cxl_region_params *p = &cxlr->params;
776+
struct cxl_decoder *cxld = cxl_rr->decoder;
777+
struct cxl_switch_decoder *cxlsd;
778+
u16 eig, peig;
779+
u8 eiw, peiw;
780+
781+
/*
782+
* While root level decoders support x3, x6, x12, switch level
783+
* decoders only support powers of 2 up to x16.
784+
*/
785+
if (!is_power_of_2(cxl_rr->nr_targets)) {
786+
dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n",
787+
dev_name(port->uport), dev_name(&port->dev),
788+
cxl_rr->nr_targets);
789+
return -EINVAL;
790+
}
791+
792+
cxlsd = to_cxl_switch_decoder(&cxld->dev);
793+
if (cxl_rr->nr_targets_set) {
794+
int i, distance;
795+
796+
distance = p->nr_targets / cxl_rr->nr_targets;
797+
for (i = 0; i < cxl_rr->nr_targets_set; i++)
798+
if (ep->dport == cxlsd->target[i]) {
799+
rc = check_last_peer(cxled, ep, cxl_rr,
800+
distance);
801+
if (rc)
802+
return rc;
803+
goto out_target_set;
804+
}
805+
goto add_target;
806+
}
807+
808+
if (is_cxl_root(parent_port)) {
809+
parent_ig = cxlrd->cxlsd.cxld.interleave_granularity;
810+
parent_iw = cxlrd->cxlsd.cxld.interleave_ways;
811+
/*
812+
* For purposes of address bit routing, use power-of-2 math for
813+
* switch ports.
814+
*/
815+
if (!is_power_of_2(parent_iw))
816+
parent_iw /= 3;
817+
} else {
818+
struct cxl_region_ref *parent_rr;
819+
struct cxl_decoder *parent_cxld;
820+
821+
parent_rr = cxl_rr_load(parent_port, cxlr);
822+
parent_cxld = parent_rr->decoder;
823+
parent_ig = parent_cxld->interleave_granularity;
824+
parent_iw = parent_cxld->interleave_ways;
825+
}
826+
827+
granularity_to_cxl(parent_ig, &peig);
828+
ways_to_cxl(parent_iw, &peiw);
829+
830+
iw = cxl_rr->nr_targets;
831+
ways_to_cxl(iw, &eiw);
832+
if (cxl_rr->nr_targets > 1) {
833+
u32 address_bit = max(peig + peiw, eiw + peig);
834+
835+
eig = address_bit - eiw + 1;
836+
} else {
837+
eiw = peiw;
838+
eig = peig;
839+
}
840+
841+
rc = cxl_to_granularity(eig, &ig);
842+
if (rc) {
843+
dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n",
844+
dev_name(port->uport), dev_name(&port->dev),
845+
256 << eig);
846+
return rc;
847+
}
848+
849+
cxld->interleave_ways = iw;
850+
cxld->interleave_granularity = ig;
851+
dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport),
852+
dev_name(&port->dev), iw, ig);
853+
add_target:
854+
if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) {
855+
dev_dbg(&cxlr->dev,
856+
"%s:%s: targets full trying to add %s:%s at %d\n",
857+
dev_name(port->uport), dev_name(&port->dev),
858+
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
859+
return -ENXIO;
860+
}
861+
cxlsd->target[cxl_rr->nr_targets_set] = ep->dport;
862+
inc = 1;
863+
out_target_set:
864+
cxl_rr->nr_targets_set += inc;
865+
dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
866+
dev_name(port->uport), dev_name(&port->dev),
867+
cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport),
868+
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
869+
870+
return 0;
871+
}
872+
873+
static void cxl_port_reset_targets(struct cxl_port *port,
874+
struct cxl_region *cxlr)
875+
{
876+
struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
877+
878+
/*
879+
* After the last endpoint has been detached the entire cxl_rr may now
880+
* be gone.
881+
*/
882+
if (cxl_rr)
883+
cxl_rr->nr_targets_set = 0;
884+
}
885+
886+
static void cxl_region_teardown_targets(struct cxl_region *cxlr)
887+
{
888+
struct cxl_region_params *p = &cxlr->params;
889+
struct cxl_endpoint_decoder *cxled;
890+
struct cxl_memdev *cxlmd;
891+
struct cxl_port *iter;
892+
struct cxl_ep *ep;
893+
int i;
894+
895+
for (i = 0; i < p->nr_targets; i++) {
896+
cxled = p->targets[i];
897+
cxlmd = cxled_to_memdev(cxled);
898+
899+
iter = cxled_to_port(cxled);
900+
while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
901+
iter = to_cxl_port(iter->dev.parent);
902+
903+
for (ep = cxl_ep_load(iter, cxlmd); iter;
904+
iter = ep->next, ep = cxl_ep_load(iter, cxlmd))
905+
cxl_port_reset_targets(iter, cxlr);
906+
}
907+
}
908+
909+
static int cxl_region_setup_targets(struct cxl_region *cxlr)
910+
{
911+
struct cxl_region_params *p = &cxlr->params;
912+
struct cxl_endpoint_decoder *cxled;
913+
struct cxl_memdev *cxlmd;
914+
struct cxl_port *iter;
915+
struct cxl_ep *ep;
916+
int i, rc;
917+
918+
for (i = 0; i < p->nr_targets; i++) {
919+
cxled = p->targets[i];
920+
cxlmd = cxled_to_memdev(cxled);
921+
922+
iter = cxled_to_port(cxled);
923+
while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
924+
iter = to_cxl_port(iter->dev.parent);
925+
926+
/*
927+
* Descend the topology tree programming targets while
928+
* looking for conflicts.
929+
*/
930+
for (ep = cxl_ep_load(iter, cxlmd); iter;
931+
iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) {
932+
rc = cxl_port_setup_targets(iter, cxlr, cxled);
933+
if (rc) {
934+
cxl_region_teardown_targets(cxlr);
935+
return rc;
936+
}
937+
}
938+
}
939+
940+
return 0;
941+
}
942+
705943
static int cxl_region_attach(struct cxl_region *cxlr,
706944
struct cxl_endpoint_decoder *cxled, int pos)
707945
{
@@ -814,8 +1052,12 @@ static int cxl_region_attach(struct cxl_region *cxlr,
8141052
cxled->pos = pos;
8151053
p->nr_targets++;
8161054

817-
if (p->nr_targets == p->interleave_ways)
1055+
if (p->nr_targets == p->interleave_ways) {
1056+
rc = cxl_region_setup_targets(cxlr);
1057+
if (rc)
1058+
goto err;
8181059
p->state = CXL_CONFIG_ACTIVE;
1060+
}
8191061

8201062
return 0;
8211063

@@ -854,8 +1096,10 @@ static void cxl_region_detach(struct cxl_endpoint_decoder *cxled)
8541096
goto out;
8551097
}
8561098

857-
if (p->state == CXL_CONFIG_ACTIVE)
1099+
if (p->state == CXL_CONFIG_ACTIVE) {
8581100
p->state = CXL_CONFIG_INTERLEAVE_ACTIVE;
1101+
cxl_region_teardown_targets(cxlr);
1102+
}
8591103
p->targets[cxled->pos] = NULL;
8601104
p->nr_targets--;
8611105

drivers/cxl/cxl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ struct cxl_ep {
491491
* @decoder: decoder assigned for @region in @port
492492
* @region: region for this reference
493493
* @endpoints: cxl_ep references for region members beneath @port
494+
* @nr_targets_set: track how many targets have been programmed during setup
494495
* @nr_eps: number of endpoints beneath @port
495496
* @nr_targets: number of distinct targets needed to reach @nr_eps
496497
*/
@@ -499,6 +500,7 @@ struct cxl_region_ref {
499500
struct cxl_decoder *decoder;
500501
struct cxl_region *region;
501502
struct xarray endpoints;
503+
int nr_targets_set;
502504
int nr_eps;
503505
int nr_targets;
504506
};

0 commit comments

Comments
 (0)