static int tb_available_bandwidth()

in tb.c [318:409]


static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
	struct tb_port *dst_port, int *available_up, int *available_down)
{
	int usb3_consumed_up, usb3_consumed_down, ret;
	struct tb_cm *tcm = tb_priv(tb);
	struct tb_tunnel *tunnel;
	struct tb_port *port;

	tb_port_dbg(dst_port, "calculating available bandwidth\n");

	tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port);
	if (tunnel) {
		ret = tb_tunnel_consumed_bandwidth(tunnel, &usb3_consumed_up,
						   &usb3_consumed_down);
		if (ret)
			return ret;
	} else {
		usb3_consumed_up = 0;
		usb3_consumed_down = 0;
	}

	*available_up = *available_down = 40000;

	/* Find the minimum available bandwidth over all links */
	tb_for_each_port_on_path(src_port, dst_port, port) {
		int link_speed, link_width, up_bw, down_bw;

		if (!tb_port_is_null(port))
			continue;

		if (tb_is_upstream_port(port)) {
			link_speed = port->sw->link_speed;
		} else {
			link_speed = tb_port_get_link_speed(port);
			if (link_speed < 0)
				return link_speed;
		}

		link_width = port->bonded ? 2 : 1;

		up_bw = link_speed * link_width * 1000; /* Mb/s */
		/* Leave 10% guard band */
		up_bw -= up_bw / 10;
		down_bw = up_bw;

		tb_port_dbg(port, "link total bandwidth %d Mb/s\n", up_bw);

		/*
		 * Find all DP tunnels that cross the port and reduce
		 * their consumed bandwidth from the available.
		 */
		list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
			int dp_consumed_up, dp_consumed_down;

			if (!tb_tunnel_is_dp(tunnel))
				continue;

			if (!tb_tunnel_port_on_path(tunnel, port))
				continue;

			ret = tb_tunnel_consumed_bandwidth(tunnel,
							   &dp_consumed_up,
							   &dp_consumed_down);
			if (ret)
				return ret;

			up_bw -= dp_consumed_up;
			down_bw -= dp_consumed_down;
		}

		/*
		 * If USB3 is tunneled from the host router down to the
		 * branch leading to port we need to take USB3 consumed
		 * bandwidth into account regardless whether it actually
		 * crosses the port.
		 */
		up_bw -= usb3_consumed_up;
		down_bw -= usb3_consumed_down;

		if (up_bw < *available_up)
			*available_up = up_bw;
		if (down_bw < *available_down)
			*available_down = down_bw;
	}

	if (*available_up < 0)
		*available_up = 0;
	if (*available_down < 0)
		*available_down = 0;

	return 0;
}