summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/tb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/tb.c')
-rw-r--r--drivers/thunderbolt/tb.c52
1 files changed, 51 insertions, 1 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 8f58b9c3ef07..bb763a5cf103 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -422,11 +422,51 @@ out:
return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN);
}
+static int tb_available_bw(struct tb_cm *tcm, struct tb_port *in,
+ struct tb_port *out)
+{
+ struct tb_switch *sw = out->sw;
+ struct tb_tunnel *tunnel;
+ int bw, available_bw = 40000;
+
+ while (sw && sw != in->sw) {
+ bw = sw->link_speed * sw->link_width * 1000; /* Mb/s */
+ /* Leave 10% guard band */
+ bw -= bw / 10;
+
+ /*
+ * Check for any active DP tunnels that go through this
+ * switch and reduce their consumed bandwidth from
+ * available.
+ */
+ list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
+ int consumed_bw;
+
+ if (!tb_tunnel_switch_on_path(tunnel, sw))
+ continue;
+
+ consumed_bw = tb_tunnel_consumed_bandwidth(tunnel);
+ if (consumed_bw < 0)
+ return consumed_bw;
+
+ bw -= consumed_bw;
+ }
+
+ if (bw < available_bw)
+ available_bw = bw;
+
+ sw = tb_switch_parent(sw);
+ }
+
+ return available_bw;
+}
+
static void tb_tunnel_dp(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
struct tb_port *port, *in, *out;
struct tb_tunnel *tunnel;
+ int available_bw;
/*
* Find pair of inactive DP IN and DP OUT adapters and then
@@ -464,7 +504,17 @@ static void tb_tunnel_dp(struct tb *tb)
return;
}
- tunnel = tb_tunnel_alloc_dp(tb, in, out);
+ /* Calculate available bandwidth between in and out */
+ available_bw = tb_available_bw(tcm, in, out);
+ if (available_bw < 0) {
+ tb_warn(tb, "failed to determine available bandwidth\n");
+ return;
+ }
+
+ tb_dbg(tb, "available bandwidth for new DP tunnel %u Mb/s\n",
+ available_bw);
+
+ tunnel = tb_tunnel_alloc_dp(tb, in, out, available_bw);
if (!tunnel) {
tb_port_dbg(out, "could not allocate DP tunnel\n");
goto dealloc_dp;