2904 lines
		
	
	
		
			87 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2904 lines
		
	
	
		
			87 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * KUnit tests
 | |
|  *
 | |
|  * Copyright (C) 2020, Intel Corporation
 | |
|  * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
 | |
|  */
 | |
| 
 | |
| #include <kunit/test.h>
 | |
| #include <linux/idr.h>
 | |
| 
 | |
| #include "tb.h"
 | |
| #include "tunnel.h"
 | |
| 
 | |
| static int __ida_init(struct kunit_resource *res, void *context)
 | |
| {
 | |
| 	struct ida *ida = context;
 | |
| 
 | |
| 	ida_init(ida);
 | |
| 	res->data = ida;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __ida_destroy(struct kunit_resource *res)
 | |
| {
 | |
| 	struct ida *ida = res->data;
 | |
| 
 | |
| 	ida_destroy(ida);
 | |
| }
 | |
| 
 | |
| static void kunit_ida_init(struct kunit *test, struct ida *ida)
 | |
| {
 | |
| 	kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida);
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
 | |
| 				      u8 upstream_port, u8 max_port_number)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 	size_t size;
 | |
| 	int i;
 | |
| 
 | |
| 	sw = kunit_kzalloc(test, sizeof(*sw), GFP_KERNEL);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->config.upstream_port_number = upstream_port;
 | |
| 	sw->config.depth = tb_route_length(route);
 | |
| 	sw->config.route_hi = upper_32_bits(route);
 | |
| 	sw->config.route_lo = lower_32_bits(route);
 | |
| 	sw->config.enabled = 0;
 | |
| 	sw->config.max_port_number = max_port_number;
 | |
| 
 | |
| 	size = (sw->config.max_port_number + 1) * sizeof(*sw->ports);
 | |
| 	sw->ports = kunit_kzalloc(test, size, GFP_KERNEL);
 | |
| 	if (!sw->ports)
 | |
| 		return NULL;
 | |
| 
 | |
| 	for (i = 0; i <= sw->config.max_port_number; i++) {
 | |
| 		sw->ports[i].sw = sw;
 | |
| 		sw->ports[i].port = i;
 | |
| 		sw->ports[i].config.port_number = i;
 | |
| 		if (i) {
 | |
| 			kunit_ida_init(test, &sw->ports[i].in_hopids);
 | |
| 			kunit_ida_init(test, &sw->ports[i].out_hopids);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_host(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_switch(test, 0, 7, 13);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->config.vendor_id = 0x8086;
 | |
| 	sw->config.device_id = 0x9a1b;
 | |
| 
 | |
| 	sw->ports[0].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[0].config.max_in_hop_id = 7;
 | |
| 	sw->ports[0].config.max_out_hop_id = 7;
 | |
| 
 | |
| 	sw->ports[1].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[1].config.max_in_hop_id = 19;
 | |
| 	sw->ports[1].config.max_out_hop_id = 19;
 | |
| 	sw->ports[1].total_credits = 60;
 | |
| 	sw->ports[1].ctl_credits = 2;
 | |
| 	sw->ports[1].dual_link_port = &sw->ports[2];
 | |
| 
 | |
| 	sw->ports[2].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[2].config.max_in_hop_id = 19;
 | |
| 	sw->ports[2].config.max_out_hop_id = 19;
 | |
| 	sw->ports[2].total_credits = 60;
 | |
| 	sw->ports[2].ctl_credits = 2;
 | |
| 	sw->ports[2].dual_link_port = &sw->ports[1];
 | |
| 	sw->ports[2].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[3].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[3].config.max_in_hop_id = 19;
 | |
| 	sw->ports[3].config.max_out_hop_id = 19;
 | |
| 	sw->ports[3].total_credits = 60;
 | |
| 	sw->ports[3].ctl_credits = 2;
 | |
| 	sw->ports[3].dual_link_port = &sw->ports[4];
 | |
| 
 | |
| 	sw->ports[4].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[4].config.max_in_hop_id = 19;
 | |
| 	sw->ports[4].config.max_out_hop_id = 19;
 | |
| 	sw->ports[4].total_credits = 60;
 | |
| 	sw->ports[4].ctl_credits = 2;
 | |
| 	sw->ports[4].dual_link_port = &sw->ports[3];
 | |
| 	sw->ports[4].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[5].config.type = TB_TYPE_DP_HDMI_IN;
 | |
| 	sw->ports[5].config.max_in_hop_id = 9;
 | |
| 	sw->ports[5].config.max_out_hop_id = 9;
 | |
| 	sw->ports[5].cap_adap = -1;
 | |
| 
 | |
| 	sw->ports[6].config.type = TB_TYPE_DP_HDMI_IN;
 | |
| 	sw->ports[6].config.max_in_hop_id = 9;
 | |
| 	sw->ports[6].config.max_out_hop_id = 9;
 | |
| 	sw->ports[6].cap_adap = -1;
 | |
| 
 | |
| 	sw->ports[7].config.type = TB_TYPE_NHI;
 | |
| 	sw->ports[7].config.max_in_hop_id = 11;
 | |
| 	sw->ports[7].config.max_out_hop_id = 11;
 | |
| 	sw->ports[7].config.nfc_credits = 0x41800000;
 | |
| 
 | |
| 	sw->ports[8].config.type = TB_TYPE_PCIE_DOWN;
 | |
| 	sw->ports[8].config.max_in_hop_id = 8;
 | |
| 	sw->ports[8].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[9].config.type = TB_TYPE_PCIE_DOWN;
 | |
| 	sw->ports[9].config.max_in_hop_id = 8;
 | |
| 	sw->ports[9].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[10].disabled = true;
 | |
| 	sw->ports[11].disabled = true;
 | |
| 
 | |
| 	sw->ports[12].config.type = TB_TYPE_USB3_DOWN;
 | |
| 	sw->ports[12].config.max_in_hop_id = 8;
 | |
| 	sw->ports[12].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[13].config.type = TB_TYPE_USB3_DOWN;
 | |
| 	sw->ports[13].config.max_in_hop_id = 8;
 | |
| 	sw->ports[13].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_host_usb4(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_host(test);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->generation = 4;
 | |
| 	sw->credit_allocation = true;
 | |
| 	sw->max_usb3_credits = 32;
 | |
| 	sw->min_dp_aux_credits = 1;
 | |
| 	sw->min_dp_main_credits = 0;
 | |
| 	sw->max_pcie_credits = 64;
 | |
| 	sw->max_dma_credits = 14;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_host_br(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_host_usb4(test);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->ports[10].config.type = TB_TYPE_DP_HDMI_IN;
 | |
| 	sw->ports[10].config.max_in_hop_id = 9;
 | |
| 	sw->ports[10].config.max_out_hop_id = 9;
 | |
| 	sw->ports[10].cap_adap = -1;
 | |
| 	sw->ports[10].disabled = false;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_dev_default(struct kunit *test,
 | |
| 					   struct tb_switch *parent,
 | |
| 					   u64 route, bool bonded)
 | |
| {
 | |
| 	struct tb_port *port, *upstream_port;
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_switch(test, route, 1, 19);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->config.vendor_id = 0x8086;
 | |
| 	sw->config.device_id = 0x15ef;
 | |
| 
 | |
| 	sw->ports[0].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[0].config.max_in_hop_id = 8;
 | |
| 	sw->ports[0].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[1].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[1].config.max_in_hop_id = 19;
 | |
| 	sw->ports[1].config.max_out_hop_id = 19;
 | |
| 	sw->ports[1].total_credits = 60;
 | |
| 	sw->ports[1].ctl_credits = 2;
 | |
| 	sw->ports[1].dual_link_port = &sw->ports[2];
 | |
| 
 | |
| 	sw->ports[2].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[2].config.max_in_hop_id = 19;
 | |
| 	sw->ports[2].config.max_out_hop_id = 19;
 | |
| 	sw->ports[2].total_credits = 60;
 | |
| 	sw->ports[2].ctl_credits = 2;
 | |
| 	sw->ports[2].dual_link_port = &sw->ports[1];
 | |
| 	sw->ports[2].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[3].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[3].config.max_in_hop_id = 19;
 | |
| 	sw->ports[3].config.max_out_hop_id = 19;
 | |
| 	sw->ports[3].total_credits = 60;
 | |
| 	sw->ports[3].ctl_credits = 2;
 | |
| 	sw->ports[3].dual_link_port = &sw->ports[4];
 | |
| 
 | |
| 	sw->ports[4].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[4].config.max_in_hop_id = 19;
 | |
| 	sw->ports[4].config.max_out_hop_id = 19;
 | |
| 	sw->ports[4].total_credits = 60;
 | |
| 	sw->ports[4].ctl_credits = 2;
 | |
| 	sw->ports[4].dual_link_port = &sw->ports[3];
 | |
| 	sw->ports[4].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[5].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[5].config.max_in_hop_id = 19;
 | |
| 	sw->ports[5].config.max_out_hop_id = 19;
 | |
| 	sw->ports[5].total_credits = 60;
 | |
| 	sw->ports[5].ctl_credits = 2;
 | |
| 	sw->ports[5].dual_link_port = &sw->ports[6];
 | |
| 
 | |
| 	sw->ports[6].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[6].config.max_in_hop_id = 19;
 | |
| 	sw->ports[6].config.max_out_hop_id = 19;
 | |
| 	sw->ports[6].total_credits = 60;
 | |
| 	sw->ports[6].ctl_credits = 2;
 | |
| 	sw->ports[6].dual_link_port = &sw->ports[5];
 | |
| 	sw->ports[6].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[7].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[7].config.max_in_hop_id = 19;
 | |
| 	sw->ports[7].config.max_out_hop_id = 19;
 | |
| 	sw->ports[7].total_credits = 60;
 | |
| 	sw->ports[7].ctl_credits = 2;
 | |
| 	sw->ports[7].dual_link_port = &sw->ports[8];
 | |
| 
 | |
| 	sw->ports[8].config.type = TB_TYPE_PORT;
 | |
| 	sw->ports[8].config.max_in_hop_id = 19;
 | |
| 	sw->ports[8].config.max_out_hop_id = 19;
 | |
| 	sw->ports[8].total_credits = 60;
 | |
| 	sw->ports[8].ctl_credits = 2;
 | |
| 	sw->ports[8].dual_link_port = &sw->ports[7];
 | |
| 	sw->ports[8].link_nr = 1;
 | |
| 
 | |
| 	sw->ports[9].config.type = TB_TYPE_PCIE_UP;
 | |
| 	sw->ports[9].config.max_in_hop_id = 8;
 | |
| 	sw->ports[9].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[10].config.type = TB_TYPE_PCIE_DOWN;
 | |
| 	sw->ports[10].config.max_in_hop_id = 8;
 | |
| 	sw->ports[10].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[11].config.type = TB_TYPE_PCIE_DOWN;
 | |
| 	sw->ports[11].config.max_in_hop_id = 8;
 | |
| 	sw->ports[11].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[12].config.type = TB_TYPE_PCIE_DOWN;
 | |
| 	sw->ports[12].config.max_in_hop_id = 8;
 | |
| 	sw->ports[12].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[13].config.type = TB_TYPE_DP_HDMI_OUT;
 | |
| 	sw->ports[13].config.max_in_hop_id = 9;
 | |
| 	sw->ports[13].config.max_out_hop_id = 9;
 | |
| 	sw->ports[13].cap_adap = -1;
 | |
| 
 | |
| 	sw->ports[14].config.type = TB_TYPE_DP_HDMI_OUT;
 | |
| 	sw->ports[14].config.max_in_hop_id = 9;
 | |
| 	sw->ports[14].config.max_out_hop_id = 9;
 | |
| 	sw->ports[14].cap_adap = -1;
 | |
| 
 | |
| 	sw->ports[15].disabled = true;
 | |
| 
 | |
| 	sw->ports[16].config.type = TB_TYPE_USB3_UP;
 | |
| 	sw->ports[16].config.max_in_hop_id = 8;
 | |
| 	sw->ports[16].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[17].config.type = TB_TYPE_USB3_DOWN;
 | |
| 	sw->ports[17].config.max_in_hop_id = 8;
 | |
| 	sw->ports[17].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[18].config.type = TB_TYPE_USB3_DOWN;
 | |
| 	sw->ports[18].config.max_in_hop_id = 8;
 | |
| 	sw->ports[18].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	sw->ports[19].config.type = TB_TYPE_USB3_DOWN;
 | |
| 	sw->ports[19].config.max_in_hop_id = 8;
 | |
| 	sw->ports[19].config.max_out_hop_id = 8;
 | |
| 
 | |
| 	if (!parent)
 | |
| 		return sw;
 | |
| 
 | |
| 	/* Link them */
 | |
| 	upstream_port = tb_upstream_port(sw);
 | |
| 	port = tb_port_at(route, parent);
 | |
| 	port->remote = upstream_port;
 | |
| 	upstream_port->remote = port;
 | |
| 	if (port->dual_link_port && upstream_port->dual_link_port) {
 | |
| 		port->dual_link_port->remote = upstream_port->dual_link_port;
 | |
| 		upstream_port->dual_link_port->remote = port->dual_link_port;
 | |
| 
 | |
| 		if (bonded) {
 | |
| 			/* Bonding is used */
 | |
| 			port->bonded = true;
 | |
| 			port->total_credits *= 2;
 | |
| 			port->dual_link_port->bonded = true;
 | |
| 			port->dual_link_port->total_credits = 0;
 | |
| 			upstream_port->bonded = true;
 | |
| 			upstream_port->total_credits *= 2;
 | |
| 			upstream_port->dual_link_port->bonded = true;
 | |
| 			upstream_port->dual_link_port->total_credits = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_dev_with_dpin(struct kunit *test,
 | |
| 					     struct tb_switch *parent,
 | |
| 					     u64 route, bool bonded)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_dev_default(test, parent, route, bonded);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->ports[13].config.type = TB_TYPE_DP_HDMI_IN;
 | |
| 	sw->ports[13].config.max_in_hop_id = 9;
 | |
| 	sw->ports[13].config.max_out_hop_id = 9;
 | |
| 
 | |
| 	sw->ports[14].config.type = TB_TYPE_DP_HDMI_IN;
 | |
| 	sw->ports[14].config.max_in_hop_id = 9;
 | |
| 	sw->ports[14].config.max_out_hop_id = 9;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_dev_without_dp(struct kunit *test,
 | |
| 					      struct tb_switch *parent,
 | |
| 					      u64 route, bool bonded)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 	int i;
 | |
| 
 | |
| 	sw = alloc_dev_default(test, parent, route, bonded);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 	/*
 | |
| 	 * Device with:
 | |
| 	 * 2x USB4 Adapters (adapters 1,2 and 3,4),
 | |
| 	 * 1x PCIe Upstream (adapter 9),
 | |
| 	 * 1x PCIe Downstream (adapter 10),
 | |
| 	 * 1x USB3 Upstream (adapter 16),
 | |
| 	 * 1x USB3 Downstream (adapter 17)
 | |
| 	 */
 | |
| 	for (i = 5; i <= 8; i++)
 | |
| 		sw->ports[i].disabled = true;
 | |
| 
 | |
| 	for (i = 11; i <= 14; i++)
 | |
| 		sw->ports[i].disabled = true;
 | |
| 
 | |
| 	sw->ports[13].cap_adap = 0;
 | |
| 	sw->ports[14].cap_adap = 0;
 | |
| 
 | |
| 	for (i = 18; i <= 19; i++)
 | |
| 		sw->ports[i].disabled = true;
 | |
| 
 | |
| 	sw->generation = 4;
 | |
| 	sw->credit_allocation = true;
 | |
| 	sw->max_usb3_credits = 109;
 | |
| 	sw->min_dp_aux_credits = 0;
 | |
| 	sw->min_dp_main_credits = 0;
 | |
| 	sw->max_pcie_credits = 30;
 | |
| 	sw->max_dma_credits = 1;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static struct tb_switch *alloc_dev_usb4(struct kunit *test,
 | |
| 					struct tb_switch *parent,
 | |
| 					u64 route, bool bonded)
 | |
| {
 | |
| 	struct tb_switch *sw;
 | |
| 
 | |
| 	sw = alloc_dev_default(test, parent, route, bonded);
 | |
| 	if (!sw)
 | |
| 		return NULL;
 | |
| 
 | |
| 	sw->generation = 4;
 | |
| 	sw->credit_allocation = true;
 | |
| 	sw->max_usb3_credits = 14;
 | |
| 	sw->min_dp_aux_credits = 1;
 | |
| 	sw->min_dp_main_credits = 18;
 | |
| 	sw->max_pcie_credits = 32;
 | |
| 	sw->max_dma_credits = 14;
 | |
| 
 | |
| 	return sw;
 | |
| }
 | |
| 
 | |
| static void tb_test_path_basic(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	struct tb_switch *host;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 
 | |
| 	src_port = &host->ports[5];
 | |
| 	dst_port = src_port;
 | |
| 
 | |
| 	p = tb_next_port_on_path(src_port, dst_port, NULL);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, p, dst_port);
 | |
| 
 | |
| 	p = tb_next_port_on_path(src_port, dst_port, p);
 | |
| 	KUNIT_EXPECT_TRUE(test, !p);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_not_connected_walk(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	struct tb_switch *host, *dev;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	/* No connection between host and dev */
 | |
| 	dev = alloc_dev_default(test, NULL, 3, true);
 | |
| 
 | |
| 	src_port = &host->ports[12];
 | |
| 	dst_port = &dev->ports[16];
 | |
| 
 | |
| 	p = tb_next_port_on_path(src_port, dst_port, NULL);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, p, src_port);
 | |
| 
 | |
| 	p = tb_next_port_on_path(src_port, dst_port, p);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, p, &host->ports[3]);
 | |
| 
 | |
| 	p = tb_next_port_on_path(src_port, dst_port, p);
 | |
| 	KUNIT_EXPECT_TRUE(test, !p);
 | |
| 
 | |
| 	/* Other direction */
 | |
| 
 | |
| 	p = tb_next_port_on_path(dst_port, src_port, NULL);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, p, dst_port);
 | |
| 
 | |
| 	p = tb_next_port_on_path(dst_port, src_port, p);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, p, &dev->ports[1]);
 | |
| 
 | |
| 	p = tb_next_port_on_path(dst_port, src_port, p);
 | |
| 	KUNIT_EXPECT_TRUE(test, !p);
 | |
| }
 | |
| 
 | |
| struct port_expectation {
 | |
| 	u64 route;
 | |
| 	u8 port;
 | |
| 	enum tb_port_type type;
 | |
| };
 | |
| 
 | |
| static void tb_test_path_single_hop_walk(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * Walks from Host PCIe downstream port to Device #1 PCIe
 | |
| 	 * upstream port.
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *   1 |
 | |
| 	 *   1 |
 | |
| 	 *  [Device]
 | |
| 	 */
 | |
| 	static const struct port_expectation test_data[] = {
 | |
| 		{ .route = 0x0, .port = 8, .type = TB_TYPE_PCIE_DOWN },
 | |
| 		{ .route = 0x0, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 9, .type = TB_TYPE_PCIE_UP },
 | |
| 	};
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 1, true);
 | |
| 
 | |
| 	src_port = &host->ports[8];
 | |
| 	dst_port = &dev->ports[9];
 | |
| 
 | |
| 	/* Walk both directions */
 | |
| 
 | |
| 	i = 0;
 | |
| 	tb_for_each_port_on_path(src_port, dst_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
 | |
| 
 | |
| 	i = ARRAY_SIZE(test_data) - 1;
 | |
| 	tb_for_each_port_on_path(dst_port, src_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i--;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, -1);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_daisy_chain_walk(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * Walks from Host DP IN to Device #2 DP OUT.
 | |
| 	 *
 | |
| 	 *           [Host]
 | |
| 	 *            1 |
 | |
| 	 *            1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /
 | |
| 	 *      1 /
 | |
| 	 * [Device #2]
 | |
| 	 */
 | |
| 	static const struct port_expectation test_data[] = {
 | |
| 		{ .route = 0x0, .port = 5, .type = TB_TYPE_DP_HDMI_IN },
 | |
| 		{ .route = 0x0, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 13, .type = TB_TYPE_DP_HDMI_OUT },
 | |
| 	};
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	struct tb_switch *host, *dev1, *dev2;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x301, true);
 | |
| 
 | |
| 	src_port = &host->ports[5];
 | |
| 	dst_port = &dev2->ports[13];
 | |
| 
 | |
| 	/* Walk both directions */
 | |
| 
 | |
| 	i = 0;
 | |
| 	tb_for_each_port_on_path(src_port, dst_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
 | |
| 
 | |
| 	i = ARRAY_SIZE(test_data) - 1;
 | |
| 	tb_for_each_port_on_path(dst_port, src_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i--;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, -1);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_simple_tree_walk(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * Walks from Host DP IN to Device #3 DP OUT.
 | |
| 	 *
 | |
| 	 *           [Host]
 | |
| 	 *            1 |
 | |
| 	 *            1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #4]
 | |
| 	 *             | 1
 | |
| 	 *         [Device #3]
 | |
| 	 */
 | |
| 	static const struct port_expectation test_data[] = {
 | |
| 		{ .route = 0x0, .port = 5, .type = TB_TYPE_DP_HDMI_IN },
 | |
| 		{ .route = 0x0, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 5, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x501, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x501, .port = 13, .type = TB_TYPE_DP_HDMI_OUT },
 | |
| 	};
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	struct tb_switch *host, *dev1, *dev3;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	alloc_dev_default(test, dev1, 0x301, true);
 | |
| 	dev3 = alloc_dev_default(test, dev1, 0x501, true);
 | |
| 	alloc_dev_default(test, dev1, 0x701, true);
 | |
| 
 | |
| 	src_port = &host->ports[5];
 | |
| 	dst_port = &dev3->ports[13];
 | |
| 
 | |
| 	/* Walk both directions */
 | |
| 
 | |
| 	i = 0;
 | |
| 	tb_for_each_port_on_path(src_port, dst_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
 | |
| 
 | |
| 	i = ARRAY_SIZE(test_data) - 1;
 | |
| 	tb_for_each_port_on_path(dst_port, src_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i--;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, -1);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_complex_tree_walk(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * Walks from Device #3 DP IN to Device #9 DP OUT.
 | |
| 	 *
 | |
| 	 *           [Host]
 | |
| 	 *            1 |
 | |
| 	 *            1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #5]
 | |
| 	 *    5 |      | 1         \ 7
 | |
| 	 *    1 |  [Device #4]      \ 1
 | |
| 	 * [Device #3]             [Device #6]
 | |
| 	 *                       3 /
 | |
| 	 *                      1 /
 | |
| 	 *                    [Device #7]
 | |
| 	 *                  3 /      | 5
 | |
| 	 *                 1 /       |
 | |
| 	 *               [Device #8] | 1
 | |
| 	 *                       [Device #9]
 | |
| 	 */
 | |
| 	static const struct port_expectation test_data[] = {
 | |
| 		{ .route = 0x50301, .port = 13, .type = TB_TYPE_DP_HDMI_IN },
 | |
| 		{ .route = 0x50301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 5, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 7, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x701, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x701, .port = 7, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x70701, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x70701, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3070701, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3070701, .port = 5, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x503070701, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x503070701, .port = 14, .type = TB_TYPE_DP_HDMI_OUT },
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev5, *dev6, *dev7, *dev9;
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x301, true);
 | |
| 	dev3 = alloc_dev_with_dpin(test, dev2, 0x50301, true);
 | |
| 	alloc_dev_default(test, dev1, 0x501, true);
 | |
| 	dev5 = alloc_dev_default(test, dev1, 0x701, true);
 | |
| 	dev6 = alloc_dev_default(test, dev5, 0x70701, true);
 | |
| 	dev7 = alloc_dev_default(test, dev6, 0x3070701, true);
 | |
| 	alloc_dev_default(test, dev7, 0x303070701, true);
 | |
| 	dev9 = alloc_dev_default(test, dev7, 0x503070701, true);
 | |
| 
 | |
| 	src_port = &dev3->ports[13];
 | |
| 	dst_port = &dev9->ports[14];
 | |
| 
 | |
| 	/* Walk both directions */
 | |
| 
 | |
| 	i = 0;
 | |
| 	tb_for_each_port_on_path(src_port, dst_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
 | |
| 
 | |
| 	i = ARRAY_SIZE(test_data) - 1;
 | |
| 	tb_for_each_port_on_path(dst_port, src_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i--;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, -1);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_max_length_walk(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5, *dev6;
 | |
| 	struct tb_switch *dev7, *dev8, *dev9, *dev10, *dev11, *dev12;
 | |
| 	struct tb_port *src_port, *dst_port, *p;
 | |
| 	int i;
 | |
| 
 | |
| 	/*
 | |
| 	 * Walks from Device #6 DP IN to Device #12 DP OUT.
 | |
| 	 *
 | |
| 	 *          [Host]
 | |
| 	 *         1 /  \ 3
 | |
| 	 *        1 /    \ 1
 | |
| 	 * [Device #1]   [Device #7]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #2]   [Device #8]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #3]   [Device #9]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #4]   [Device #10]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #5]   [Device #11]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #6]   [Device #12]
 | |
| 	 */
 | |
| 	static const struct port_expectation test_data[] = {
 | |
| 		{ .route = 0x30303030301, .port = 13, .type = TB_TYPE_DP_HDMI_IN },
 | |
| 		{ .route = 0x30303030301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303030301, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303030301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3030301, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3030301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30301, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x301, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x1, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x0, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x0, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30303, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30303, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3030303, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x3030303, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303030303, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x303030303, .port = 3, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30303030303, .port = 1, .type = TB_TYPE_PORT },
 | |
| 		{ .route = 0x30303030303, .port = 13, .type = TB_TYPE_DP_HDMI_OUT },
 | |
| 	};
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x301, true);
 | |
| 	dev3 = alloc_dev_default(test, dev2, 0x30301, true);
 | |
| 	dev4 = alloc_dev_default(test, dev3, 0x3030301, true);
 | |
| 	dev5 = alloc_dev_default(test, dev4, 0x303030301, true);
 | |
| 	dev6 = alloc_dev_with_dpin(test, dev5, 0x30303030301, true);
 | |
| 	dev7 = alloc_dev_default(test, host, 0x3, true);
 | |
| 	dev8 = alloc_dev_default(test, dev7, 0x303, true);
 | |
| 	dev9 = alloc_dev_default(test, dev8, 0x30303, true);
 | |
| 	dev10 = alloc_dev_default(test, dev9, 0x3030303, true);
 | |
| 	dev11 = alloc_dev_default(test, dev10, 0x303030303, true);
 | |
| 	dev12 = alloc_dev_default(test, dev11, 0x30303030303, true);
 | |
| 
 | |
| 	src_port = &dev6->ports[13];
 | |
| 	dst_port = &dev12->ports[13];
 | |
| 
 | |
| 	/* Walk both directions */
 | |
| 
 | |
| 	i = 0;
 | |
| 	tb_for_each_port_on_path(src_port, dst_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
 | |
| 
 | |
| 	i = ARRAY_SIZE(test_data) - 1;
 | |
| 	tb_for_each_port_on_path(dst_port, src_port, p) {
 | |
| 		KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data));
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, p->port, test_data[i].port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type,
 | |
| 				test_data[i].type);
 | |
| 		i--;
 | |
| 	}
 | |
| 
 | |
| 	KUNIT_EXPECT_EQ(test, i, -1);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_not_connected(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2;
 | |
| 	struct tb_port *down, *up;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x3, false);
 | |
| 	/* Not connected to anything */
 | |
| 	dev2 = alloc_dev_default(test, NULL, 0x303, false);
 | |
| 
 | |
| 	down = &dev1->ports[10];
 | |
| 	up = &dev2->ports[9];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, down, 8, up, 8, 0, "PCIe Down");
 | |
| 	KUNIT_ASSERT_NULL(test, path);
 | |
| 	path = tb_path_alloc(NULL, down, 8, up, 8, 1, "PCIe Down");
 | |
| 	KUNIT_ASSERT_NULL(test, path);
 | |
| }
 | |
| 
 | |
| struct hop_expectation {
 | |
| 	u64 route;
 | |
| 	u8 in_port;
 | |
| 	enum tb_port_type in_type;
 | |
| 	u8 out_port;
 | |
| 	enum tb_port_type out_type;
 | |
| };
 | |
| 
 | |
| static void tb_test_path_not_bonded_lane0(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * PCIe path from host to device using lane 0.
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *   3 |: 4
 | |
| 	 *   1 |: 2
 | |
| 	 *  [Device]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 9,
 | |
| 			.in_type = TB_TYPE_PCIE_DOWN,
 | |
| 			.out_port = 3,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x3,
 | |
| 			.in_port = 1,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 9,
 | |
| 			.out_type = TB_TYPE_PCIE_UP,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *down, *up;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 0x3, false);
 | |
| 
 | |
| 	down = &host->ports[9];
 | |
| 	up = &dev->ports[9];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, down, 8, up, 8, 0, "PCIe Down");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_not_bonded_lane1(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * DP Video path from host to device using lane 1. Paths like
 | |
| 	 * these are only used with Thunderbolt 1 devices where lane
 | |
| 	 * bonding is not possible. USB4 specifically does not allow
 | |
| 	 * paths like this (you either use lane 0 where lane 1 is
 | |
| 	 * disabled or both lanes are bonded).
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *   1 :| 2
 | |
| 	 *   1 :| 2
 | |
| 	 *  [Device]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 5,
 | |
| 			.in_type = TB_TYPE_DP_HDMI_IN,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x1,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 13,
 | |
| 			.out_type = TB_TYPE_DP_HDMI_OUT,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 0x1, false);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev->ports[13];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_not_bonded_lane1_chain(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * DP Video path from host to device 3 using lane 1.
 | |
| 	 *
 | |
| 	 *    [Host]
 | |
| 	 *    1 :| 2
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #1]
 | |
| 	 *    7 :| 8
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #2]
 | |
| 	 *    5 :| 6
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #3]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 5,
 | |
| 			.in_type = TB_TYPE_DP_HDMI_IN,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x1,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 8,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x701,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 6,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x50701,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 13,
 | |
| 			.out_type = TB_TYPE_DP_HDMI_OUT,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, false);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, false);
 | |
| 	dev3 = alloc_dev_default(test, dev2, 0x50701, false);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev3->ports[13];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_not_bonded_lane1_chain_reverse(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * DP Video path from device 3 to host using lane 1.
 | |
| 	 *
 | |
| 	 *    [Host]
 | |
| 	 *    1 :| 2
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #1]
 | |
| 	 *    7 :| 8
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #2]
 | |
| 	 *    5 :| 6
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #3]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x50701,
 | |
| 			.in_port = 13,
 | |
| 			.in_type = TB_TYPE_DP_HDMI_IN,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x701,
 | |
| 			.in_port = 6,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x1,
 | |
| 			.in_port = 8,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 5,
 | |
| 			.out_type = TB_TYPE_DP_HDMI_IN,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, false);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, false);
 | |
| 	dev3 = alloc_dev_with_dpin(test, dev2, 0x50701, false);
 | |
| 
 | |
| 	in = &dev3->ports[13];
 | |
| 	out = &host->ports[5];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_mixed_chain(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * DP Video path from host to device 4 where first and last link
 | |
| 	 * is bonded.
 | |
| 	 *
 | |
| 	 *    [Host]
 | |
| 	 *    1 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #1]
 | |
| 	 *    7 :| 8
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #2]
 | |
| 	 *    5 :| 6
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #3]
 | |
| 	 *    3 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #4]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 5,
 | |
| 			.in_type = TB_TYPE_DP_HDMI_IN,
 | |
| 			.out_port = 1,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x1,
 | |
| 			.in_port = 1,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 8,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x701,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 6,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x50701,
 | |
| 			.in_port = 2,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 3,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x3050701,
 | |
| 			.in_port = 1,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 13,
 | |
| 			.out_type = TB_TYPE_DP_HDMI_OUT,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, false);
 | |
| 	dev3 = alloc_dev_default(test, dev2, 0x50701, false);
 | |
| 	dev4 = alloc_dev_default(test, dev3, 0x3050701, true);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev4->ports[13];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_path_mixed_chain_reverse(struct kunit *test)
 | |
| {
 | |
| 	/*
 | |
| 	 * DP Video path from device 4 to host where first and last link
 | |
| 	 * is bonded.
 | |
| 	 *
 | |
| 	 *    [Host]
 | |
| 	 *    1 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #1]
 | |
| 	 *    7 :| 8
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #2]
 | |
| 	 *    5 :| 6
 | |
| 	 *    1 :| 2
 | |
| 	 *  [Device #3]
 | |
| 	 *    3 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #4]
 | |
| 	 */
 | |
| 	static const struct hop_expectation test_data[] = {
 | |
| 		{
 | |
| 			.route = 0x3050701,
 | |
| 			.in_port = 13,
 | |
| 			.in_type = TB_TYPE_DP_HDMI_OUT,
 | |
| 			.out_port = 1,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x50701,
 | |
| 			.in_port = 3,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x701,
 | |
| 			.in_port = 6,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 2,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x1,
 | |
| 			.in_port = 8,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 1,
 | |
| 			.out_type = TB_TYPE_PORT,
 | |
| 		},
 | |
| 		{
 | |
| 			.route = 0x0,
 | |
| 			.in_port = 1,
 | |
| 			.in_type = TB_TYPE_PORT,
 | |
| 			.out_port = 5,
 | |
| 			.out_type = TB_TYPE_DP_HDMI_IN,
 | |
| 		},
 | |
| 	};
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_path *path;
 | |
| 	int i;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, false);
 | |
| 	dev3 = alloc_dev_default(test, dev2, 0x50701, false);
 | |
| 	dev4 = alloc_dev_default(test, dev3, 0x3050701, true);
 | |
| 
 | |
| 	in = &dev4->ports[13];
 | |
| 	out = &host->ports[5];
 | |
| 
 | |
| 	path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video");
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, path);
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data));
 | |
| 	for (i = 0; i < ARRAY_SIZE(test_data); i++) {
 | |
| 		const struct tb_port *in_port, *out_port;
 | |
| 
 | |
| 		in_port = path->hops[i].in_port;
 | |
| 		out_port = path->hops[i].out_port;
 | |
| 
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type,
 | |
| 				test_data[i].in_type);
 | |
| 		KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route);
 | |
| 		KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port);
 | |
| 		KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type,
 | |
| 				test_data[i].out_type);
 | |
| 	}
 | |
| 	tb_path_free(path);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_pcie(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2;
 | |
| 	struct tb_tunnel *tunnel1, *tunnel2;
 | |
| 	struct tb_port *down, *up;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create PCIe tunnel between host and two devices.
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *    1 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #1]
 | |
| 	 *    5 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x501, true);
 | |
| 
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev1->ports[9];
 | |
| 	tunnel1 = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel1);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_PCI);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->npaths, 2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[0].in_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[1].out_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->paths[1]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[0].in_port, up);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[1].out_port, down);
 | |
| 
 | |
| 	down = &dev1->ports[10];
 | |
| 	up = &dev2->ports[9];
 | |
| 	tunnel2 = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel2);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_PCI);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->npaths, 2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[0].in_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[1].out_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->paths[1]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel2);
 | |
| 	tb_tunnel_free(tunnel1);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dp(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DP tunnel between Host and Device
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *   1 |
 | |
| 	 *   1 |
 | |
| 	 *  [Device]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 0x3, true);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev->ports[13];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[1].out_port, in);
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dp_chain(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev4;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DP tunnel from Host DP IN to Device #4 DP OUT.
 | |
| 	 *
 | |
| 	 *           [Host]
 | |
| 	 *            1 |
 | |
| 	 *            1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #4]
 | |
| 	 *             | 1
 | |
| 	 *         [Device #3]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	alloc_dev_default(test, dev1, 0x301, true);
 | |
| 	alloc_dev_default(test, dev1, 0x501, true);
 | |
| 	dev4 = alloc_dev_default(test, dev1, 0x701, true);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev4->ports[14];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[2].out_port, in);
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dp_tree(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev5;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DP tunnel from Device #2 DP IN to Device #5 DP OUT.
 | |
| 	 *
 | |
| 	 *          [Host]
 | |
| 	 *           3 |
 | |
| 	 *           1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #4]
 | |
| 	 *             | 1
 | |
| 	 *         [Device #3]
 | |
| 	 *             | 5
 | |
| 	 *             | 1
 | |
| 	 *         [Device #5]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x3, true);
 | |
| 	dev2 = alloc_dev_with_dpin(test, dev1, 0x303, true);
 | |
| 	dev3 = alloc_dev_default(test, dev1, 0x503, true);
 | |
| 	alloc_dev_default(test, dev1, 0x703, true);
 | |
| 	dev5 = alloc_dev_default(test, dev3, 0x50503, true);
 | |
| 
 | |
| 	in = &dev2->ports[13];
 | |
| 	out = &dev5->ports[13];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 4);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[3].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 4);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[3].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 4);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[3].out_port, in);
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dp_max_length(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5, *dev6;
 | |
| 	struct tb_switch *dev7, *dev8, *dev9, *dev10, *dev11, *dev12;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 * Creates DP tunnel from Device #6 to Device #12.
 | |
| 	 *
 | |
| 	 *          [Host]
 | |
| 	 *         1 /  \ 3
 | |
| 	 *        1 /    \ 1
 | |
| 	 * [Device #1]   [Device #7]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #2]   [Device #8]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #3]   [Device #9]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #4]   [Device #10]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #5]   [Device #11]
 | |
| 	 *     3 |           | 3
 | |
| 	 *     1 |           | 1
 | |
| 	 * [Device #6]   [Device #12]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x301, true);
 | |
| 	dev3 = alloc_dev_default(test, dev2, 0x30301, true);
 | |
| 	dev4 = alloc_dev_default(test, dev3, 0x3030301, true);
 | |
| 	dev5 = alloc_dev_default(test, dev4, 0x303030301, true);
 | |
| 	dev6 = alloc_dev_with_dpin(test, dev5, 0x30303030301, true);
 | |
| 	dev7 = alloc_dev_default(test, host, 0x3, true);
 | |
| 	dev8 = alloc_dev_default(test, dev7, 0x303, true);
 | |
| 	dev9 = alloc_dev_default(test, dev8, 0x30303, true);
 | |
| 	dev10 = alloc_dev_default(test, dev9, 0x3030303, true);
 | |
| 	dev11 = alloc_dev_default(test, dev10, 0x303030303, true);
 | |
| 	dev12 = alloc_dev_default(test, dev11, 0x30303030303, true);
 | |
| 
 | |
| 	in = &dev6->ports[13];
 | |
| 	out = &dev12->ports[13];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 13);
 | |
| 	/* First hop */
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in);
 | |
| 	/* Middle */
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[6].in_port,
 | |
| 			    &host->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[6].out_port,
 | |
| 			    &host->ports[3]);
 | |
| 	/* Last */
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[12].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 13);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[6].in_port,
 | |
| 			    &host->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[6].out_port,
 | |
| 			    &host->ports[3]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[12].out_port, out);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 13);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[6].in_port,
 | |
| 			    &host->ports[3]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[6].out_port,
 | |
| 			    &host->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[12].out_port, in);
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_3dp(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5;
 | |
| 	struct tb_port *in1, *in2, *in3, *out1, *out2, *out3;
 | |
| 	struct tb_tunnel *tunnel1, *tunnel2, *tunnel3;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create 3 DP tunnels from Host to Devices #2, #5 and #4.
 | |
| 	 *
 | |
| 	 *          [Host]
 | |
| 	 *           3 |
 | |
| 	 *           1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #4]
 | |
| 	 *             | 1
 | |
| 	 *         [Device #3]
 | |
| 	 *             | 5
 | |
| 	 *             | 1
 | |
| 	 *         [Device #5]
 | |
| 	 */
 | |
| 	host = alloc_host_br(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x3, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x303, true);
 | |
| 	dev3 = alloc_dev_default(test, dev1, 0x503, true);
 | |
| 	dev4 = alloc_dev_default(test, dev1, 0x703, true);
 | |
| 	dev5 = alloc_dev_default(test, dev3, 0x50503, true);
 | |
| 
 | |
| 	in1 = &host->ports[5];
 | |
| 	in2 = &host->ports[6];
 | |
| 	in3 = &host->ports[10];
 | |
| 
 | |
| 	out1 = &dev2->ports[13];
 | |
| 	out2 = &dev5->ports[13];
 | |
| 	out3 = &dev4->ports[14];
 | |
| 
 | |
| 	tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, out1);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
 | |
| 
 | |
| 	tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, out2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
 | |
| 
 | |
| 	tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel3->dst_port, out3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel3->npaths, 3);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel3->paths[0]->path_length, 3);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel2);
 | |
| 	tb_tunnel_free(tunnel1);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_usb3(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2;
 | |
| 	struct tb_tunnel *tunnel1, *tunnel2;
 | |
| 	struct tb_port *down, *up;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create USB3 tunnel between host and two devices.
 | |
| 	 *
 | |
| 	 *   [Host]
 | |
| 	 *    1 |
 | |
| 	 *    1 |
 | |
| 	 *  [Device #1]
 | |
| 	 *          \ 7
 | |
| 	 *           \ 1
 | |
| 	 *         [Device #2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, true);
 | |
| 
 | |
| 	down = &host->ports[12];
 | |
| 	up = &dev1->ports[16];
 | |
| 	tunnel1 = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel1);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_USB3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->npaths, 2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[0].in_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[1].out_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->paths[1]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[0].in_port, up);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[1].out_port, down);
 | |
| 
 | |
| 	down = &dev1->ports[17];
 | |
| 	up = &dev2->ports[16];
 | |
| 	tunnel2 = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel2);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_USB3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->npaths, 2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[0].in_port, down);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[1].out_port, up);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->paths[1]->path_length, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel2);
 | |
| 	tb_tunnel_free(tunnel1);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_port_on_path(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5;
 | |
| 	struct tb_port *in, *out, *port;
 | |
| 	struct tb_tunnel *dp_tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 *          [Host]
 | |
| 	 *           3 |
 | |
| 	 *           1 |
 | |
| 	 *         [Device #1]
 | |
| 	 *       3 /   | 5  \ 7
 | |
| 	 *      1 /    |     \ 1
 | |
| 	 * [Device #2] |    [Device #4]
 | |
| 	 *             | 1
 | |
| 	 *         [Device #3]
 | |
| 	 *             | 5
 | |
| 	 *             | 1
 | |
| 	 *         [Device #5]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x3, true);
 | |
| 	dev2 = alloc_dev_with_dpin(test, dev1, 0x303, true);
 | |
| 	dev3 = alloc_dev_default(test, dev1, 0x503, true);
 | |
| 	dev4 = alloc_dev_default(test, dev1, 0x703, true);
 | |
| 	dev5 = alloc_dev_default(test, dev3, 0x50503, true);
 | |
| 
 | |
| 	in = &dev2->ports[13];
 | |
| 	out = &dev5->ports[13];
 | |
| 
 | |
| 	dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
 | |
| 
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, out));
 | |
| 
 | |
| 	port = &host->ports[8];
 | |
| 	KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &host->ports[3];
 | |
| 	KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev1->ports[1];
 | |
| 	KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev1->ports[3];
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev1->ports[5];
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev1->ports[7];
 | |
| 	KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev3->ports[1];
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev5->ports[1];
 | |
| 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	port = &dev4->ports[1];
 | |
| 	KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
 | |
| 
 | |
| 	tb_tunnel_free(dp_tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dma(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_switch *host;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DMA tunnel from NHI to port 1 and back.
 | |
| 	 *
 | |
| 	 *   [Host 1]
 | |
| 	 *    1 ^ In HopID 1 -> Out HopID 8
 | |
| 	 *      |
 | |
| 	 *      v In HopID 8 -> Out HopID 1
 | |
| 	 * ............ Domain border
 | |
| 	 *      |
 | |
| 	 *   [Host 2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &host->ports[1];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 2);
 | |
| 	/* RX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 1);
 | |
| 	/* TX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].out_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].next_hop_index, 8);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dma_rx(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_switch *host;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DMA RX tunnel from port 1 to NHI.
 | |
| 	 *
 | |
| 	 *   [Host 1]
 | |
| 	 *    1 ^
 | |
| 	 *      |
 | |
| 	 *      | In HopID 15 -> Out HopID 2
 | |
| 	 * ............ Domain border
 | |
| 	 *      |
 | |
| 	 *   [Host 2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &host->ports[1];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 2);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 1);
 | |
| 	/* RX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 15);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 2);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dma_tx(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_switch *host;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DMA TX tunnel from NHI to port 1.
 | |
| 	 *
 | |
| 	 *   [Host 1]
 | |
| 	 *    1 | In HopID 2 -> Out HopID 15
 | |
| 	 *      |
 | |
| 	 *      v
 | |
| 	 * ............ Domain border
 | |
| 	 *      |
 | |
| 	 *   [Host 2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &host->ports[1];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 2, -1, -1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 1);
 | |
| 	/* TX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 2);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 15);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dma_chain(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev1, *dev2;
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create DMA tunnel from NHI to Device #2 port 3 and back.
 | |
| 	 *
 | |
| 	 *   [Host 1]
 | |
| 	 *    1 ^ In HopID 1 -> Out HopID x
 | |
| 	 *      |
 | |
| 	 *    1 | In HopID x -> Out HopID 1
 | |
| 	 *  [Device #1]
 | |
| 	 *         7 \
 | |
| 	 *          1 \
 | |
| 	 *         [Device #2]
 | |
| 	 *           3 | In HopID x -> Out HopID 8
 | |
| 	 *             |
 | |
| 	 *             v In HopID 8 -> Out HopID x
 | |
| 	 * ............ Domain border
 | |
| 	 *             |
 | |
| 	 *          [Host 2]
 | |
| 	 */
 | |
| 	host = alloc_host(test);
 | |
| 	dev1 = alloc_dev_default(test, host, 0x1, true);
 | |
| 	dev2 = alloc_dev_default(test, dev1, 0x701, true);
 | |
| 
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &dev2->ports[3];
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, 2);
 | |
| 	/* RX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port,
 | |
| 			    &dev2->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].in_port,
 | |
| 			    &dev1->ports[7]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].out_port,
 | |
| 			    &dev1->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].in_port,
 | |
| 			    &host->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].out_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[2].next_hop_index, 1);
 | |
| 	/* TX path */
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 3);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].in_port,
 | |
| 			    &dev1->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].out_port,
 | |
| 			    &dev1->ports[7]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].in_port,
 | |
| 			    &dev2->ports[1]);
 | |
| 	KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, port);
 | |
| 	KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[2].next_hop_index, 8);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_tunnel_dma_match(struct kunit *test)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_switch *host;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &host->ports[1];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, 15, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, 1, 15, 1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, -1, 8, -1));
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, -1, -1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 11);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 11));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 11));
 | |
| 	KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 10, 11));
 | |
| 	KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_legacy_not_bonded(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 0x1, false);
 | |
| 
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev->ports[9];
 | |
| 	tunnel = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
 | |
| 
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_legacy_bonded(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host(test);
 | |
| 	dev = alloc_dev_default(test, host, 0x1, true);
 | |
| 
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev->ports[9];
 | |
| 	tunnel = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_pcie(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev->ports[9];
 | |
| 	tunnel = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_without_dp(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_without_dp(test, host, 0x1, true);
 | |
| 
 | |
| 	/*
 | |
| 	 * The device has no DP therefore baMinDPmain = baMinDPaux = 0
 | |
| 	 *
 | |
| 	 * Create PCIe path with buffers less than baMaxPCIe.
 | |
| 	 *
 | |
| 	 * For a device with buffers configurations:
 | |
| 	 * baMaxUSB3 = 109
 | |
| 	 * baMinDPaux = 0
 | |
| 	 * baMinDPmain = 0
 | |
| 	 * baMaxPCIe = 30
 | |
| 	 * baMaxHI = 1
 | |
| 	 * Remaining Buffers = Total - (CP + DP) = 120 - (2 + 0) = 118
 | |
| 	 * PCIe Credits = Max(6, Min(baMaxPCIe, Remaining Buffers - baMaxUSB3)
 | |
| 	 *		= Max(6, Min(30, 9) = 9
 | |
| 	 */
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev->ports[9];
 | |
| 	tunnel = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_TRUE(test, tunnel != NULL);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	/* PCIe downstream path */
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 9U);
 | |
| 
 | |
| 	/* PCIe upstream path */
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_dp(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev->ports[14];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
 | |
| 
 | |
| 	/* Video (main) path */
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
 | |
| 
 | |
| 	/* AUX TX */
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	/* AUX RX */
 | |
| 	path = tunnel->paths[2];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_usb3(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	down = &host->ports[12];
 | |
| 	up = &dev->ports[16];
 | |
| 	tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_dma(struct kunit *test)
 | |
| {
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &dev->ports[3];
 | |
| 
 | |
| 	tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	/* DMA RX */
 | |
| 	path = tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	/* DMA TX */
 | |
| 	path = tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel);
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_dma_multiple(struct kunit *test)
 | |
| {
 | |
| 	struct tb_tunnel *tunnel1, *tunnel2, *tunnel3;
 | |
| 	struct tb_switch *host, *dev;
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &dev->ports[3];
 | |
| 
 | |
| 	/*
 | |
| 	 * Create three DMA tunnels through the same ports. With the
 | |
| 	 * default buffers we should be able to create two and the last
 | |
| 	 * one fails.
 | |
| 	 *
 | |
| 	 * For default host we have following buffers for DMA:
 | |
| 	 *
 | |
| 	 *   120 - (2 + 2 * (1 + 0) + 32 + 64 + spare) = 20
 | |
| 	 *
 | |
| 	 * For device we have following:
 | |
| 	 *
 | |
| 	 *  120 - (2 + 2 * (1 + 18) + 14 + 32 + spare) = 34
 | |
| 	 *
 | |
| 	 * spare = 14 + 1 = 15
 | |
| 	 *
 | |
| 	 * So on host the first tunnel gets 14 and the second gets the
 | |
| 	 * remaining 1 and then we run out of buffers.
 | |
| 	 */
 | |
| 	tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel1);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel1->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel1->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	path = tunnel1->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel2);
 | |
| 	KUNIT_ASSERT_EQ(test, tunnel2->npaths, (size_t)2);
 | |
| 
 | |
| 	path = tunnel2->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	path = tunnel2->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3);
 | |
| 	KUNIT_ASSERT_NULL(test, tunnel3);
 | |
| 
 | |
| 	/*
 | |
| 	 * Release the first DMA tunnel. That should make 14 buffers
 | |
| 	 * available for the next tunnel.
 | |
| 	 */
 | |
| 	tb_tunnel_free(tunnel1);
 | |
| 
 | |
| 	tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, tunnel3);
 | |
| 
 | |
| 	path = tunnel3->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	path = tunnel3->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	tb_tunnel_free(tunnel3);
 | |
| 	tb_tunnel_free(tunnel2);
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_PCIE_TUNNEL(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *pcie_tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	down = &host->ports[8];
 | |
| 	up = &dev->ports[9];
 | |
| 	pcie_tunnel = tb_tunnel_alloc_pci(NULL, up, down);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, pcie_tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, pcie_tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = pcie_tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	path = pcie_tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
 | |
| 
 | |
| 	return pcie_tunnel;
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *dp_tunnel1;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	in = &host->ports[5];
 | |
| 	out = &dev->ports[13];
 | |
| 	dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1);
 | |
| 	KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
 | |
| 
 | |
| 	path = dp_tunnel1->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
 | |
| 
 | |
| 	path = dp_tunnel1->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	path = dp_tunnel1->paths[2];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	return dp_tunnel1;
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *in, *out;
 | |
| 	struct tb_tunnel *dp_tunnel2;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	in = &host->ports[6];
 | |
| 	out = &dev->ports[14];
 | |
| 	dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2);
 | |
| 	KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
 | |
| 
 | |
| 	path = dp_tunnel2->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
 | |
| 
 | |
| 	path = dp_tunnel2->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	path = dp_tunnel2->paths[2];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	return dp_tunnel2;
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_USB3_TUNNEL(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *up, *down;
 | |
| 	struct tb_tunnel *usb3_tunnel;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	down = &host->ports[12];
 | |
| 	up = &dev->ports[16];
 | |
| 	usb3_tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, usb3_tunnel);
 | |
| 	KUNIT_ASSERT_EQ(test, usb3_tunnel->npaths, (size_t)2);
 | |
| 
 | |
| 	path = usb3_tunnel->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	path = usb3_tunnel->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
 | |
| 
 | |
| 	return usb3_tunnel;
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_DMA_TUNNEL1(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *dma_tunnel1;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &dev->ports[3];
 | |
| 	dma_tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dma_tunnel1);
 | |
| 	KUNIT_ASSERT_EQ(test, dma_tunnel1->npaths, (size_t)2);
 | |
| 
 | |
| 	path = dma_tunnel1->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	path = dma_tunnel1->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
 | |
| 
 | |
| 	return dma_tunnel1;
 | |
| }
 | |
| 
 | |
| static struct tb_tunnel *TB_TEST_DMA_TUNNEL2(struct kunit *test,
 | |
| 			struct tb_switch *host, struct tb_switch *dev)
 | |
| {
 | |
| 	struct tb_port *nhi, *port;
 | |
| 	struct tb_tunnel *dma_tunnel2;
 | |
| 	struct tb_path *path;
 | |
| 
 | |
| 	nhi = &host->ports[7];
 | |
| 	port = &dev->ports[3];
 | |
| 	dma_tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dma_tunnel2);
 | |
| 	KUNIT_ASSERT_EQ(test, dma_tunnel2->npaths, (size_t)2);
 | |
| 
 | |
| 	path = dma_tunnel2->paths[0];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	path = dma_tunnel2->paths[1];
 | |
| 	KUNIT_ASSERT_EQ(test, path->path_length, 2);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
 | |
| 	KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
 | |
| 
 | |
| 	return dma_tunnel2;
 | |
| }
 | |
| 
 | |
| static void tb_test_credit_alloc_all(struct kunit *test)
 | |
| {
 | |
| 	struct tb_tunnel *pcie_tunnel, *dp_tunnel1, *dp_tunnel2, *usb3_tunnel;
 | |
| 	struct tb_tunnel *dma_tunnel1, *dma_tunnel2;
 | |
| 	struct tb_switch *host, *dev;
 | |
| 
 | |
| 	/*
 | |
| 	 * Create PCIe, 2 x DP, USB 3.x and two DMA tunnels from host to
 | |
| 	 * device. Expectation is that all these can be established with
 | |
| 	 * the default credit allocation found in Intel hardware.
 | |
| 	 */
 | |
| 
 | |
| 	host = alloc_host_usb4(test);
 | |
| 	dev = alloc_dev_usb4(test, host, 0x1, true);
 | |
| 
 | |
| 	pcie_tunnel = TB_TEST_PCIE_TUNNEL(test, host, dev);
 | |
| 	dp_tunnel1 = TB_TEST_DP_TUNNEL1(test, host, dev);
 | |
| 	dp_tunnel2 = TB_TEST_DP_TUNNEL2(test, host, dev);
 | |
| 	usb3_tunnel = TB_TEST_USB3_TUNNEL(test, host, dev);
 | |
| 	dma_tunnel1 = TB_TEST_DMA_TUNNEL1(test, host, dev);
 | |
| 	dma_tunnel2 = TB_TEST_DMA_TUNNEL2(test, host, dev);
 | |
| 
 | |
| 	tb_tunnel_free(dma_tunnel2);
 | |
| 	tb_tunnel_free(dma_tunnel1);
 | |
| 	tb_tunnel_free(usb3_tunnel);
 | |
| 	tb_tunnel_free(dp_tunnel2);
 | |
| 	tb_tunnel_free(dp_tunnel1);
 | |
| 	tb_tunnel_free(pcie_tunnel);
 | |
| }
 | |
| 
 | |
| static const u32 root_directory[] = {
 | |
| 	0x55584401,	/* "UXD" v1 */
 | |
| 	0x00000018,	/* Root directory length */
 | |
| 	0x76656e64,	/* "vend" */
 | |
| 	0x6f726964,	/* "orid" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x00000a27,	/* Immediate value, ! Vendor ID */
 | |
| 	0x76656e64,	/* "vend" */
 | |
| 	0x6f726964,	/* "orid" */
 | |
| 	0x74000003,	/* "t" R 3 */
 | |
| 	0x0000001a,	/* Text leaf offset, (“Apple Inc.”) */
 | |
| 	0x64657669,	/* "devi" */
 | |
| 	0x63656964,	/* "ceid" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x0000000a,	/* Immediate value, ! Device ID */
 | |
| 	0x64657669,	/* "devi" */
 | |
| 	0x63656964,	/* "ceid" */
 | |
| 	0x74000003,	/* "t" R 3 */
 | |
| 	0x0000001d,	/* Text leaf offset, (“Macintosh”) */
 | |
| 	0x64657669,	/* "devi" */
 | |
| 	0x63657276,	/* "cerv" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x80000100,	/* Immediate value, Device Revision */
 | |
| 	0x6e657477,	/* "netw" */
 | |
| 	0x6f726b00,	/* "ork" */
 | |
| 	0x44000014,	/* "D" R 20 */
 | |
| 	0x00000021,	/* Directory data offset, (Network Directory) */
 | |
| 	0x4170706c,	/* "Appl" */
 | |
| 	0x6520496e,	/* "e In" */
 | |
| 	0x632e0000,	/* "c." ! */
 | |
| 	0x4d616369,	/* "Maci" */
 | |
| 	0x6e746f73,	/* "ntos" */
 | |
| 	0x68000000,	/* "h" */
 | |
| 	0x00000000,	/* padding */
 | |
| 	0xca8961c6,	/* Directory UUID, Network Directory */
 | |
| 	0x9541ce1c,	/* Directory UUID, Network Directory */
 | |
| 	0x5949b8bd,	/* Directory UUID, Network Directory */
 | |
| 	0x4f5a5f2e,	/* Directory UUID, Network Directory */
 | |
| 	0x70727463,	/* "prtc" */
 | |
| 	0x69640000,	/* "id" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x00000001,	/* Immediate value, Network Protocol ID */
 | |
| 	0x70727463,	/* "prtc" */
 | |
| 	0x76657273,	/* "vers" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x00000001,	/* Immediate value, Network Protocol Version */
 | |
| 	0x70727463,	/* "prtc" */
 | |
| 	0x72657673,	/* "revs" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x00000001,	/* Immediate value, Network Protocol Revision */
 | |
| 	0x70727463,	/* "prtc" */
 | |
| 	0x73746e73,	/* "stns" */
 | |
| 	0x76000001,	/* "v" R 1 */
 | |
| 	0x00000000,	/* Immediate value, Network Protocol Settings */
 | |
| };
 | |
| 
 | |
| static const uuid_t network_dir_uuid =
 | |
| 	UUID_INIT(0xc66189ca, 0x1cce, 0x4195,
 | |
| 		  0xbd, 0xb8, 0x49, 0x59, 0x2e, 0x5f, 0x5a, 0x4f);
 | |
| 
 | |
| static void tb_test_property_parse(struct kunit *test)
 | |
| {
 | |
| 	struct tb_property_dir *dir, *network_dir;
 | |
| 	struct tb_property *p;
 | |
| 
 | |
| 	dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory));
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dir);
 | |
| 
 | |
| 	p = tb_property_find(dir, "foo", TB_PROPERTY_TYPE_TEXT);
 | |
| 	KUNIT_ASSERT_NULL(test, p);
 | |
| 
 | |
| 	p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_STREQ(test, p->value.text, "Apple Inc.");
 | |
| 
 | |
| 	p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0xa27);
 | |
| 
 | |
| 	p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_STREQ(test, p->value.text, "Macintosh");
 | |
| 
 | |
| 	p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0xa);
 | |
| 
 | |
| 	p = tb_property_find(dir, "missing", TB_PROPERTY_TYPE_DIRECTORY);
 | |
| 	KUNIT_ASSERT_NULL(test, p);
 | |
| 
 | |
| 	p = tb_property_find(dir, "network", TB_PROPERTY_TYPE_DIRECTORY);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 
 | |
| 	network_dir = p->value.dir;
 | |
| 	KUNIT_EXPECT_TRUE(test, uuid_equal(network_dir->uuid, &network_dir_uuid));
 | |
| 
 | |
| 	p = tb_property_find(network_dir, "prtcid", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
 | |
| 
 | |
| 	p = tb_property_find(network_dir, "prtcvers", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
 | |
| 
 | |
| 	p = tb_property_find(network_dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
 | |
| 
 | |
| 	p = tb_property_find(network_dir, "prtcstns", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, p);
 | |
| 	KUNIT_EXPECT_EQ(test, p->value.immediate, 0x0);
 | |
| 
 | |
| 	p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_VALUE);
 | |
| 	KUNIT_EXPECT_TRUE(test, !p);
 | |
| 	p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_TEXT);
 | |
| 	KUNIT_EXPECT_TRUE(test, !p);
 | |
| 
 | |
| 	tb_property_free_dir(dir);
 | |
| }
 | |
| 
 | |
| static void tb_test_property_format(struct kunit *test)
 | |
| {
 | |
| 	struct tb_property_dir *dir;
 | |
| 	ssize_t block_len;
 | |
| 	u32 *block;
 | |
| 	int ret, i;
 | |
| 
 | |
| 	dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory));
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dir);
 | |
| 
 | |
| 	ret = tb_property_format_dir(dir, NULL, 0);
 | |
| 	KUNIT_ASSERT_EQ(test, ret, ARRAY_SIZE(root_directory));
 | |
| 
 | |
| 	block_len = ret;
 | |
| 
 | |
| 	block = kunit_kzalloc(test, block_len * sizeof(u32), GFP_KERNEL);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, block);
 | |
| 
 | |
| 	ret = tb_property_format_dir(dir, block, block_len);
 | |
| 	KUNIT_EXPECT_EQ(test, ret, 0);
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(root_directory); i++)
 | |
| 		KUNIT_EXPECT_EQ(test, root_directory[i], block[i]);
 | |
| 
 | |
| 	tb_property_free_dir(dir);
 | |
| }
 | |
| 
 | |
| static void compare_dirs(struct kunit *test, struct tb_property_dir *d1,
 | |
| 			 struct tb_property_dir *d2)
 | |
| {
 | |
| 	struct tb_property *p1, *p2, *tmp;
 | |
| 	int n1, n2, i;
 | |
| 
 | |
| 	if (d1->uuid) {
 | |
| 		KUNIT_ASSERT_NOT_NULL(test, d2->uuid);
 | |
| 		KUNIT_ASSERT_TRUE(test, uuid_equal(d1->uuid, d2->uuid));
 | |
| 	} else {
 | |
| 		KUNIT_ASSERT_NULL(test, d2->uuid);
 | |
| 	}
 | |
| 
 | |
| 	n1 = 0;
 | |
| 	tb_property_for_each(d1, tmp)
 | |
| 		n1++;
 | |
| 	KUNIT_ASSERT_NE(test, n1, 0);
 | |
| 
 | |
| 	n2 = 0;
 | |
| 	tb_property_for_each(d2, tmp)
 | |
| 		n2++;
 | |
| 	KUNIT_ASSERT_NE(test, n2, 0);
 | |
| 
 | |
| 	KUNIT_ASSERT_EQ(test, n1, n2);
 | |
| 
 | |
| 	p1 = NULL;
 | |
| 	p2 = NULL;
 | |
| 	for (i = 0; i < n1; i++) {
 | |
| 		p1 = tb_property_get_next(d1, p1);
 | |
| 		KUNIT_ASSERT_NOT_NULL(test, p1);
 | |
| 		p2 = tb_property_get_next(d2, p2);
 | |
| 		KUNIT_ASSERT_NOT_NULL(test, p2);
 | |
| 
 | |
| 		KUNIT_ASSERT_STREQ(test, &p1->key[0], &p2->key[0]);
 | |
| 		KUNIT_ASSERT_EQ(test, p1->type, p2->type);
 | |
| 		KUNIT_ASSERT_EQ(test, p1->length, p2->length);
 | |
| 
 | |
| 		switch (p1->type) {
 | |
| 		case TB_PROPERTY_TYPE_DIRECTORY:
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p1->value.dir);
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p2->value.dir);
 | |
| 			compare_dirs(test, p1->value.dir, p2->value.dir);
 | |
| 			break;
 | |
| 
 | |
| 		case TB_PROPERTY_TYPE_DATA:
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p1->value.data);
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p2->value.data);
 | |
| 			KUNIT_ASSERT_TRUE(test,
 | |
| 				!memcmp(p1->value.data, p2->value.data,
 | |
| 					p1->length * 4)
 | |
| 			);
 | |
| 			break;
 | |
| 
 | |
| 		case TB_PROPERTY_TYPE_TEXT:
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p1->value.text);
 | |
| 			KUNIT_ASSERT_NOT_NULL(test, p2->value.text);
 | |
| 			KUNIT_ASSERT_STREQ(test, p1->value.text, p2->value.text);
 | |
| 			break;
 | |
| 
 | |
| 		case TB_PROPERTY_TYPE_VALUE:
 | |
| 			KUNIT_ASSERT_EQ(test, p1->value.immediate,
 | |
| 					p2->value.immediate);
 | |
| 			break;
 | |
| 		default:
 | |
| 			KUNIT_FAIL(test, "unexpected property type");
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void tb_test_property_copy(struct kunit *test)
 | |
| {
 | |
| 	struct tb_property_dir *src, *dst;
 | |
| 	u32 *block;
 | |
| 	int ret, i;
 | |
| 
 | |
| 	src = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory));
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, src);
 | |
| 
 | |
| 	dst = tb_property_copy_dir(src);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, dst);
 | |
| 
 | |
| 	/* Compare the structures */
 | |
| 	compare_dirs(test, src, dst);
 | |
| 
 | |
| 	/* Compare the resulting property block */
 | |
| 	ret = tb_property_format_dir(dst, NULL, 0);
 | |
| 	KUNIT_ASSERT_EQ(test, ret, ARRAY_SIZE(root_directory));
 | |
| 
 | |
| 	block = kunit_kzalloc(test, sizeof(root_directory), GFP_KERNEL);
 | |
| 	KUNIT_ASSERT_NOT_NULL(test, block);
 | |
| 
 | |
| 	ret = tb_property_format_dir(dst, block, ARRAY_SIZE(root_directory));
 | |
| 	KUNIT_EXPECT_TRUE(test, !ret);
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(root_directory); i++)
 | |
| 		KUNIT_EXPECT_EQ(test, root_directory[i], block[i]);
 | |
| 
 | |
| 	tb_property_free_dir(dst);
 | |
| 	tb_property_free_dir(src);
 | |
| }
 | |
| 
 | |
| static struct kunit_case tb_test_cases[] = {
 | |
| 	KUNIT_CASE(tb_test_path_basic),
 | |
| 	KUNIT_CASE(tb_test_path_not_connected_walk),
 | |
| 	KUNIT_CASE(tb_test_path_single_hop_walk),
 | |
| 	KUNIT_CASE(tb_test_path_daisy_chain_walk),
 | |
| 	KUNIT_CASE(tb_test_path_simple_tree_walk),
 | |
| 	KUNIT_CASE(tb_test_path_complex_tree_walk),
 | |
| 	KUNIT_CASE(tb_test_path_max_length_walk),
 | |
| 	KUNIT_CASE(tb_test_path_not_connected),
 | |
| 	KUNIT_CASE(tb_test_path_not_bonded_lane0),
 | |
| 	KUNIT_CASE(tb_test_path_not_bonded_lane1),
 | |
| 	KUNIT_CASE(tb_test_path_not_bonded_lane1_chain),
 | |
| 	KUNIT_CASE(tb_test_path_not_bonded_lane1_chain_reverse),
 | |
| 	KUNIT_CASE(tb_test_path_mixed_chain),
 | |
| 	KUNIT_CASE(tb_test_path_mixed_chain_reverse),
 | |
| 	KUNIT_CASE(tb_test_tunnel_pcie),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dp),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dp_chain),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dp_tree),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dp_max_length),
 | |
| 	KUNIT_CASE(tb_test_tunnel_3dp),
 | |
| 	KUNIT_CASE(tb_test_tunnel_port_on_path),
 | |
| 	KUNIT_CASE(tb_test_tunnel_usb3),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dma),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dma_rx),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dma_tx),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dma_chain),
 | |
| 	KUNIT_CASE(tb_test_tunnel_dma_match),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_legacy_not_bonded),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_legacy_bonded),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_pcie),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_without_dp),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_dp),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_usb3),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_dma),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_dma_multiple),
 | |
| 	KUNIT_CASE(tb_test_credit_alloc_all),
 | |
| 	KUNIT_CASE(tb_test_property_parse),
 | |
| 	KUNIT_CASE(tb_test_property_format),
 | |
| 	KUNIT_CASE(tb_test_property_copy),
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| static struct kunit_suite tb_test_suite = {
 | |
| 	.name = "thunderbolt",
 | |
| 	.test_cases = tb_test_cases,
 | |
| };
 | |
| 
 | |
| kunit_test_suite(tb_test_suite);
 |