in tb.c [868:972]
static void tb_tunnel_dp(struct tb *tb)
{
int available_up, available_down, ret;
struct tb_cm *tcm = tb_priv(tb);
struct tb_port *port, *in, *out;
struct tb_tunnel *tunnel;
if (!tb_acpi_may_tunnel_dp()) {
tb_dbg(tb, "DP tunneling disabled, not creating tunnel\n");
return;
}
/*
* Find pair of inactive DP IN and DP OUT adapters and then
* establish a DP tunnel between them.
*/
tb_dbg(tb, "looking for DP IN <-> DP OUT pairs:\n");
in = NULL;
out = NULL;
list_for_each_entry(port, &tcm->dp_resources, list) {
if (!tb_port_is_dpin(port))
continue;
if (tb_port_is_enabled(port)) {
tb_port_dbg(port, "in use\n");
continue;
}
tb_port_dbg(port, "DP IN available\n");
out = tb_find_dp_out(tb, port);
if (out) {
in = port;
break;
}
}
if (!in) {
tb_dbg(tb, "no suitable DP IN adapter available, not tunneling\n");
return;
}
if (!out) {
tb_dbg(tb, "no suitable DP OUT adapter available, not tunneling\n");
return;
}
/*
* DP stream needs the domain to be active so runtime resume
* both ends of the tunnel.
*
* This should bring the routers in the middle active as well
* and keeps the domain from runtime suspending while the DP
* tunnel is active.
*/
pm_runtime_get_sync(&in->sw->dev);
pm_runtime_get_sync(&out->sw->dev);
if (tb_switch_alloc_dp_resource(in->sw, in)) {
tb_port_dbg(in, "no resource available for DP IN, not tunneling\n");
goto err_rpm_put;
}
/* Make all unused USB3 bandwidth available for the new DP tunnel */
ret = tb_release_unused_usb3_bandwidth(tb, in, out);
if (ret) {
tb_warn(tb, "failed to release unused bandwidth\n");
goto err_dealloc_dp;
}
ret = tb_available_bandwidth(tb, in, out, &available_up,
&available_down);
if (ret)
goto err_reclaim;
tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
available_up, available_down);
tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down);
if (!tunnel) {
tb_port_dbg(out, "could not allocate DP tunnel\n");
goto err_reclaim;
}
if (tb_tunnel_activate(tunnel)) {
tb_port_info(out, "DP tunnel activation failed, aborting\n");
goto err_free;
}
list_add_tail(&tunnel->list, &tcm->tunnel_list);
tb_reclaim_usb3_bandwidth(tb, in, out);
return;
err_free:
tb_tunnel_free(tunnel);
err_reclaim:
tb_reclaim_usb3_bandwidth(tb, in, out);
err_dealloc_dp:
tb_switch_dealloc_dp_resource(in->sw, in);
err_rpm_put:
pm_runtime_mark_last_busy(&out->sw->dev);
pm_runtime_put_autosuspend(&out->sw->dev);
pm_runtime_mark_last_busy(&in->sw->dev);
pm_runtime_put_autosuspend(&in->sw->dev);
}