mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
thunderbolt: Notify userspace about software CM tunneling events
This adds notification whenever software connection manager activates, changes or deactivates a tunnel, and also if there is limitation in bandwidth. The notification looks like below: TUNNEL_EVENT=activated|changed|deactivated|low bandwidth| insufficient bandwidth TUNNEL_DETAILS=0:12 <-> 1:20 (USB3) Userspace can then listen these and inform user if needed. For example if there is not enough bandwidth, it can show warning dialog to the user. Signed-off-by: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
parent
cdf9956b69
commit
785da9e6a1
@ -952,6 +952,15 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
|
||||
tb_port_dbg(up, "available bandwidth for new USB3 tunnel %d/%d Mb/s\n",
|
||||
available_up, available_down);
|
||||
|
||||
/*
|
||||
* If the available bandwidth is less than 1.5 Gb/s notify
|
||||
* userspace that the connected isochronous device may not work
|
||||
* properly.
|
||||
*/
|
||||
if (available_up < 1500 || available_down < 1500)
|
||||
tb_tunnel_event(tb, TB_TUNNEL_LOW_BANDWIDTH, TB_TUNNEL_USB3,
|
||||
down, up);
|
||||
|
||||
tunnel = tb_tunnel_alloc_usb3(tb, up, down, available_up,
|
||||
available_down);
|
||||
if (!tunnel) {
|
||||
@ -2000,8 +2009,10 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
|
||||
|
||||
ret = tb_available_bandwidth(tb, in, out, &available_up, &available_down,
|
||||
true);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
|
||||
goto err_reclaim_usb;
|
||||
}
|
||||
|
||||
tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
|
||||
available_up, available_down);
|
||||
@ -2622,8 +2633,12 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
|
||||
}
|
||||
}
|
||||
|
||||
return tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
ret = tb_tunnel_alloc_bandwidth(tunnel, requested_up,
|
||||
requested_down);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2699,6 +2714,7 @@ fail:
|
||||
"failing the request by rewriting allocated %d/%d Mb/s\n",
|
||||
allocated_up, allocated_down);
|
||||
tb_tunnel_alloc_bandwidth(tunnel, &allocated_up, &allocated_down);
|
||||
tb_tunnel_event(tb, TB_TUNNEL_NO_BANDWIDTH, TB_TUNNEL_DP, in, out);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -100,6 +100,14 @@ MODULE_PARM_DESC(bw_alloc_mode,
|
||||
|
||||
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
|
||||
|
||||
static const char * const tb_event_names[] = {
|
||||
[TB_TUNNEL_ACTIVATED] = "activated",
|
||||
[TB_TUNNEL_CHANGED] = "changed",
|
||||
[TB_TUNNEL_DEACTIVATED] = "deactivated",
|
||||
[TB_TUNNEL_LOW_BANDWIDTH] = "low bandwidth",
|
||||
[TB_TUNNEL_NO_BANDWIDTH] = "insufficient bandwidth",
|
||||
};
|
||||
|
||||
/* Synchronizes kref_get()/put() of struct tb_tunnel */
|
||||
static DEFINE_MUTEX(tb_tunnel_lock);
|
||||
|
||||
@ -220,6 +228,72 @@ void tb_tunnel_put(struct tb_tunnel *tunnel)
|
||||
mutex_unlock(&tb_tunnel_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_tunnel_event() - Notify userspace about tunneling event
|
||||
* @tb: Domain where the event occurred
|
||||
* @event: Event that happened
|
||||
* @type: Type of the tunnel in question
|
||||
* @src_port: Tunnel source port (can be %NULL)
|
||||
* @dst_port: Tunnel destination port (can be %NULL)
|
||||
*
|
||||
* Notifies userspace about tunneling @event in the domain. The tunnel
|
||||
* does not need to exist (e.g the tunnel was not activated because
|
||||
* there is not enough bandwidth). If the @src_port and @dst_port are
|
||||
* given fill in full %TUNNEL_DETAILS environment variable. Otherwise
|
||||
* uses the shorter one (just the tunnel type).
|
||||
*/
|
||||
void tb_tunnel_event(struct tb *tb, enum tb_tunnel_event event,
|
||||
enum tb_tunnel_type type,
|
||||
const struct tb_port *src_port,
|
||||
const struct tb_port *dst_port)
|
||||
{
|
||||
char *envp[3] = { NULL };
|
||||
|
||||
if (WARN_ON_ONCE(event >= ARRAY_SIZE(tb_event_names)))
|
||||
return;
|
||||
if (WARN_ON_ONCE(type >= ARRAY_SIZE(tb_tunnel_names)))
|
||||
return;
|
||||
|
||||
envp[0] = kasprintf(GFP_KERNEL, "TUNNEL_EVENT=%s", tb_event_names[event]);
|
||||
if (!envp[0])
|
||||
return;
|
||||
|
||||
if (src_port != NULL && dst_port != NULL) {
|
||||
envp[1] = kasprintf(GFP_KERNEL, "TUNNEL_DETAILS=%llx:%u <-> %llx:%u (%s)",
|
||||
tb_route(src_port->sw), src_port->port,
|
||||
tb_route(dst_port->sw), dst_port->port,
|
||||
tb_tunnel_names[type]);
|
||||
} else {
|
||||
envp[1] = kasprintf(GFP_KERNEL, "TUNNEL_DETAILS=(%s)",
|
||||
tb_tunnel_names[type]);
|
||||
}
|
||||
|
||||
if (envp[1])
|
||||
tb_domain_event(tb, envp);
|
||||
|
||||
kfree(envp[1]);
|
||||
kfree(envp[0]);
|
||||
}
|
||||
|
||||
static inline void tb_tunnel_set_active(struct tb_tunnel *tunnel, bool active)
|
||||
{
|
||||
if (active) {
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_ACTIVATED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
} else {
|
||||
tunnel->state = TB_TUNNEL_INACTIVE;
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_DEACTIVATED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tb_tunnel_changed(struct tb_tunnel *tunnel)
|
||||
{
|
||||
tb_tunnel_event(tunnel->tb, TB_TUNNEL_CHANGED, tunnel->type,
|
||||
tunnel->src_port, tunnel->dst_port);
|
||||
}
|
||||
|
||||
static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable)
|
||||
{
|
||||
struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw);
|
||||
@ -992,7 +1066,7 @@ static void tb_dp_dprx_work(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
}
|
||||
mutex_unlock(&tb->lock);
|
||||
}
|
||||
@ -2326,7 +2400,7 @@ int tb_tunnel_activate(struct tb_tunnel *tunnel)
|
||||
}
|
||||
}
|
||||
|
||||
tunnel->state = TB_TUNNEL_ACTIVE;
|
||||
tb_tunnel_set_active(tunnel, true);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -2356,7 +2430,7 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
|
||||
if (tunnel->post_deactivate)
|
||||
tunnel->post_deactivate(tunnel);
|
||||
|
||||
tunnel->state = TB_TUNNEL_INACTIVE;
|
||||
tb_tunnel_set_active(tunnel, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2449,8 +2523,16 @@ int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
if (!tb_tunnel_is_active(tunnel))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (tunnel->alloc_bandwidth)
|
||||
return tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
|
||||
if (tunnel->alloc_bandwidth) {
|
||||
int ret;
|
||||
|
||||
ret = tunnel->alloc_bandwidth(tunnel, alloc_up, alloc_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tb_tunnel_changed(tunnel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -194,6 +194,29 @@ static inline bool tb_tunnel_direction_downstream(const struct tb_tunnel *tunnel
|
||||
tunnel->dst_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* enum tb_tunnel_event - Tunnel related events
|
||||
* @TB_TUNNEL_ACTIVATED: A tunnel was activated
|
||||
* @TB_TUNNEL_CHANGED: There is a tunneling change in the domain. Includes
|
||||
* full %TUNNEL_DETAILS if the tunnel in question is known
|
||||
* (ICM does not provide that information).
|
||||
* @TB_TUNNEL_DEACTIVATED: A tunnel was torn down
|
||||
* @TB_TUNNEL_LOW_BANDWIDTH: Tunnel bandwidth is not optimal
|
||||
* @TB_TUNNEL_NO_BANDWIDTH: There is not enough bandwidth for a tunnel
|
||||
*/
|
||||
enum tb_tunnel_event {
|
||||
TB_TUNNEL_ACTIVATED,
|
||||
TB_TUNNEL_CHANGED,
|
||||
TB_TUNNEL_DEACTIVATED,
|
||||
TB_TUNNEL_LOW_BANDWIDTH,
|
||||
TB_TUNNEL_NO_BANDWIDTH,
|
||||
};
|
||||
|
||||
void tb_tunnel_event(struct tb *tb, enum tb_tunnel_event event,
|
||||
enum tb_tunnel_type type,
|
||||
const struct tb_port *src_port,
|
||||
const struct tb_port *dst_port);
|
||||
|
||||
const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel);
|
||||
|
||||
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
|
||||
|
Loading…
Reference in New Issue
Block a user