Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/165784337827.1758207.132121746122685208.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+259 -11
+4
drivers/cxl/core/core.h
··· 42 42 extern struct rw_semaphore cxl_dpa_rwsem; 43 43 44 44 bool is_switch_decoder(struct device *dev); 45 + struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev); 45 46 static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port, 46 47 struct cxl_memdev *cxlmd) 47 48 { 49 + if (!port) 50 + return NULL; 51 + 48 52 return xa_load(&port->endpoints, (unsigned long)&cxlmd->dev); 49 53 } 50 54
+1 -3
drivers/cxl/core/port.c
··· 146 146 return offset; 147 147 } 148 148 149 - static struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev); 150 - 151 149 static ssize_t target_list_show(struct device *dev, 152 150 struct device_attribute *attr, char *buf) 153 151 { ··· 470 472 } 471 473 EXPORT_SYMBOL_NS_GPL(to_cxl_endpoint_decoder, CXL); 472 474 473 - static struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev) 475 + struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev) 474 476 { 475 477 if (dev_WARN_ONCE(dev, !is_switch_decoder(dev), 476 478 "not a cxl_switch_decoder device\n"))
+252 -8
drivers/cxl/core/region.c
··· 491 491 return NULL; 492 492 cxl_rr->port = port; 493 493 cxl_rr->region = cxlr; 494 + cxl_rr->nr_targets = 1; 494 495 xa_init(&cxl_rr->endpoints); 495 496 496 497 rc = xa_insert(&port->regions, (unsigned long)cxlr, cxl_rr, GFP_KERNEL); ··· 532 531 struct cxl_decoder *cxld = cxl_rr->decoder; 533 532 struct cxl_ep *ep = cxl_ep_load(port, cxled_to_memdev(cxled)); 534 533 535 - rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep, 536 - GFP_KERNEL); 537 - if (rc) 538 - return rc; 534 + if (ep) { 535 + rc = xa_insert(&cxl_rr->endpoints, (unsigned long)cxled, ep, 536 + GFP_KERNEL); 537 + if (rc) 538 + return rc; 539 + } 539 540 cxl_rr->nr_eps++; 540 541 541 542 if (!cxld->region) { ··· 658 655 goto out_erase; 659 656 } 660 657 658 + dev_dbg(&cxlr->dev, 659 + "%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n", 660 + dev_name(port->uport), dev_name(&port->dev), 661 + dev_name(&cxld->dev), dev_name(&cxlmd->dev), 662 + dev_name(&cxled->cxld.dev), pos, 663 + ep ? ep->next ? dev_name(ep->next->uport) : 664 + dev_name(&cxlmd->dev) : 665 + "none", 666 + cxl_rr->nr_eps, cxl_rr->nr_targets); 667 + 661 668 return 0; 662 669 out_erase: 663 670 if (cxl_rr->nr_eps == 0) ··· 686 673 struct cxl_endpoint_decoder *cxled) 687 674 { 688 675 struct cxl_region_ref *cxl_rr; 689 - struct cxl_ep *ep; 676 + struct cxl_ep *ep = NULL; 690 677 691 678 lockdep_assert_held_write(&cxl_region_rwsem); 692 679 ··· 694 681 if (!cxl_rr) 695 682 return; 696 683 697 - ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled); 684 + /* 685 + * Endpoint ports do not carry cxl_ep references, and they 686 + * never target more than one endpoint by definition 687 + */ 688 + if (cxl_rr->decoder == &cxled->cxld) 689 + cxl_rr->nr_eps--; 690 + else 691 + ep = xa_erase(&cxl_rr->endpoints, (unsigned long)cxled); 698 692 if (ep) { 699 693 struct cxl_ep *ep_iter; 700 694 unsigned long index; ··· 720 700 721 701 if (cxl_rr->nr_eps == 0) 722 702 free_region_ref(cxl_rr); 703 + } 704 + 705 + static int check_last_peer(struct cxl_endpoint_decoder *cxled, 706 + struct cxl_ep *ep, struct cxl_region_ref *cxl_rr, 707 + int distance) 708 + { 709 + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 710 + struct cxl_region *cxlr = cxl_rr->region; 711 + struct cxl_region_params *p = &cxlr->params; 712 + struct cxl_endpoint_decoder *cxled_peer; 713 + struct cxl_port *port = cxl_rr->port; 714 + struct cxl_memdev *cxlmd_peer; 715 + struct cxl_ep *ep_peer; 716 + int pos = cxled->pos; 717 + 718 + /* 719 + * If this position wants to share a dport with the last endpoint mapped 720 + * then that endpoint, at index 'position - distance', must also be 721 + * mapped by this dport. 722 + */ 723 + if (pos < distance) { 724 + dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n", 725 + dev_name(port->uport), dev_name(&port->dev), 726 + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 727 + return -ENXIO; 728 + } 729 + cxled_peer = p->targets[pos - distance]; 730 + cxlmd_peer = cxled_to_memdev(cxled_peer); 731 + ep_peer = cxl_ep_load(port, cxlmd_peer); 732 + if (ep->dport != ep_peer->dport) { 733 + dev_dbg(&cxlr->dev, 734 + "%s:%s: %s:%s pos %d mismatched peer %s:%s\n", 735 + dev_name(port->uport), dev_name(&port->dev), 736 + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos, 737 + dev_name(&cxlmd_peer->dev), 738 + dev_name(&cxled_peer->cxld.dev)); 739 + return -ENXIO; 740 + } 741 + 742 + return 0; 743 + } 744 + 745 + static int cxl_port_setup_targets(struct cxl_port *port, 746 + struct cxl_region *cxlr, 747 + struct cxl_endpoint_decoder *cxled) 748 + { 749 + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); 750 + int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos; 751 + struct cxl_port *parent_port = to_cxl_port(port->dev.parent); 752 + struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); 753 + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 754 + struct cxl_ep *ep = cxl_ep_load(port, cxlmd); 755 + struct cxl_region_params *p = &cxlr->params; 756 + struct cxl_decoder *cxld = cxl_rr->decoder; 757 + struct cxl_switch_decoder *cxlsd; 758 + u16 eig, peig; 759 + u8 eiw, peiw; 760 + 761 + /* 762 + * While root level decoders support x3, x6, x12, switch level 763 + * decoders only support powers of 2 up to x16. 764 + */ 765 + if (!is_power_of_2(cxl_rr->nr_targets)) { 766 + dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n", 767 + dev_name(port->uport), dev_name(&port->dev), 768 + cxl_rr->nr_targets); 769 + return -EINVAL; 770 + } 771 + 772 + cxlsd = to_cxl_switch_decoder(&cxld->dev); 773 + if (cxl_rr->nr_targets_set) { 774 + int i, distance; 775 + 776 + distance = p->nr_targets / cxl_rr->nr_targets; 777 + for (i = 0; i < cxl_rr->nr_targets_set; i++) 778 + if (ep->dport == cxlsd->target[i]) { 779 + rc = check_last_peer(cxled, ep, cxl_rr, 780 + distance); 781 + if (rc) 782 + return rc; 783 + goto out_target_set; 784 + } 785 + goto add_target; 786 + } 787 + 788 + if (is_cxl_root(parent_port)) { 789 + parent_ig = cxlrd->cxlsd.cxld.interleave_granularity; 790 + parent_iw = cxlrd->cxlsd.cxld.interleave_ways; 791 + /* 792 + * For purposes of address bit routing, use power-of-2 math for 793 + * switch ports. 794 + */ 795 + if (!is_power_of_2(parent_iw)) 796 + parent_iw /= 3; 797 + } else { 798 + struct cxl_region_ref *parent_rr; 799 + struct cxl_decoder *parent_cxld; 800 + 801 + parent_rr = cxl_rr_load(parent_port, cxlr); 802 + parent_cxld = parent_rr->decoder; 803 + parent_ig = parent_cxld->interleave_granularity; 804 + parent_iw = parent_cxld->interleave_ways; 805 + } 806 + 807 + granularity_to_cxl(parent_ig, &peig); 808 + ways_to_cxl(parent_iw, &peiw); 809 + 810 + iw = cxl_rr->nr_targets; 811 + ways_to_cxl(iw, &eiw); 812 + if (cxl_rr->nr_targets > 1) { 813 + u32 address_bit = max(peig + peiw, eiw + peig); 814 + 815 + eig = address_bit - eiw + 1; 816 + } else { 817 + eiw = peiw; 818 + eig = peig; 819 + } 820 + 821 + rc = cxl_to_granularity(eig, &ig); 822 + if (rc) { 823 + dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n", 824 + dev_name(port->uport), dev_name(&port->dev), 825 + 256 << eig); 826 + return rc; 827 + } 828 + 829 + cxld->interleave_ways = iw; 830 + cxld->interleave_granularity = ig; 831 + dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport), 832 + dev_name(&port->dev), iw, ig); 833 + add_target: 834 + if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) { 835 + dev_dbg(&cxlr->dev, 836 + "%s:%s: targets full trying to add %s:%s at %d\n", 837 + dev_name(port->uport), dev_name(&port->dev), 838 + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 839 + return -ENXIO; 840 + } 841 + cxlsd->target[cxl_rr->nr_targets_set] = ep->dport; 842 + inc = 1; 843 + out_target_set: 844 + cxl_rr->nr_targets_set += inc; 845 + dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n", 846 + dev_name(port->uport), dev_name(&port->dev), 847 + cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport), 848 + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos); 849 + 850 + return 0; 851 + } 852 + 853 + static void cxl_port_reset_targets(struct cxl_port *port, 854 + struct cxl_region *cxlr) 855 + { 856 + struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr); 857 + 858 + /* 859 + * After the last endpoint has been detached the entire cxl_rr may now 860 + * be gone. 861 + */ 862 + if (cxl_rr) 863 + cxl_rr->nr_targets_set = 0; 864 + } 865 + 866 + static void cxl_region_teardown_targets(struct cxl_region *cxlr) 867 + { 868 + struct cxl_region_params *p = &cxlr->params; 869 + struct cxl_endpoint_decoder *cxled; 870 + struct cxl_memdev *cxlmd; 871 + struct cxl_port *iter; 872 + struct cxl_ep *ep; 873 + int i; 874 + 875 + for (i = 0; i < p->nr_targets; i++) { 876 + cxled = p->targets[i]; 877 + cxlmd = cxled_to_memdev(cxled); 878 + 879 + iter = cxled_to_port(cxled); 880 + while (!is_cxl_root(to_cxl_port(iter->dev.parent))) 881 + iter = to_cxl_port(iter->dev.parent); 882 + 883 + for (ep = cxl_ep_load(iter, cxlmd); iter; 884 + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) 885 + cxl_port_reset_targets(iter, cxlr); 886 + } 887 + } 888 + 889 + static int cxl_region_setup_targets(struct cxl_region *cxlr) 890 + { 891 + struct cxl_region_params *p = &cxlr->params; 892 + struct cxl_endpoint_decoder *cxled; 893 + struct cxl_memdev *cxlmd; 894 + struct cxl_port *iter; 895 + struct cxl_ep *ep; 896 + int i, rc; 897 + 898 + for (i = 0; i < p->nr_targets; i++) { 899 + cxled = p->targets[i]; 900 + cxlmd = cxled_to_memdev(cxled); 901 + 902 + iter = cxled_to_port(cxled); 903 + while (!is_cxl_root(to_cxl_port(iter->dev.parent))) 904 + iter = to_cxl_port(iter->dev.parent); 905 + 906 + /* 907 + * Descend the topology tree programming targets while 908 + * looking for conflicts. 909 + */ 910 + for (ep = cxl_ep_load(iter, cxlmd); iter; 911 + iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) { 912 + rc = cxl_port_setup_targets(iter, cxlr, cxled); 913 + if (rc) { 914 + cxl_region_teardown_targets(cxlr); 915 + return rc; 916 + } 917 + } 918 + } 919 + 920 + return 0; 723 921 } 724 922 725 923 static int cxl_region_attach(struct cxl_region *cxlr, ··· 1052 814 cxled->pos = pos; 1053 815 p->nr_targets++; 1054 816 1055 - if (p->nr_targets == p->interleave_ways) 817 + if (p->nr_targets == p->interleave_ways) { 818 + rc = cxl_region_setup_targets(cxlr); 819 + if (rc) 820 + goto err; 1056 821 p->state = CXL_CONFIG_ACTIVE; 822 + } 1057 823 1058 824 return 0; 1059 825 ··· 1096 854 goto out; 1097 855 } 1098 856 1099 - if (p->state == CXL_CONFIG_ACTIVE) 857 + if (p->state == CXL_CONFIG_ACTIVE) { 1100 858 p->state = CXL_CONFIG_INTERLEAVE_ACTIVE; 859 + cxl_region_teardown_targets(cxlr); 860 + } 1101 861 p->targets[cxled->pos] = NULL; 1102 862 p->nr_targets--; 1103 863
+2
drivers/cxl/cxl.h
··· 491 491 * @decoder: decoder assigned for @region in @port 492 492 * @region: region for this reference 493 493 * @endpoints: cxl_ep references for region members beneath @port 494 + * @nr_targets_set: track how many targets have been programmed during setup 494 495 * @nr_eps: number of endpoints beneath @port 495 496 * @nr_targets: number of distinct targets needed to reach @nr_eps 496 497 */ ··· 500 499 struct cxl_decoder *decoder; 501 500 struct cxl_region *region; 502 501 struct xarray endpoints; 502 + int nr_targets_set; 503 503 int nr_eps; 504 504 int nr_targets; 505 505 };