diff --git a/drivers/thunderbolt/apple-dpin-state.c b/drivers/thunderbolt/apple-dpin-state.c index 2c3eadc4ad0f1..1a2721d6e84a4 100644 --- a/drivers/thunderbolt/apple-dpin-state.c +++ b/drivers/thunderbolt/apple-dpin-state.c @@ -38,14 +38,14 @@ const struct apple_dpin_policy apple_dpin_m3 = { * writes only DPTX_INACTIVE (+0x0c bit 0) and polls its ACK (+0x10 bit 0), * gated on HPD. The DP IN interrupts are not set up (nothing handles them), * and a tunnel that DCP never activated is torn down without touching the - * block at all. Both DP IN streams of an ATC PHY need one link rate, so - * every tunnel gets HBR2 at most. + * block at all. Both DP IN streams of an ATC PHY need one link rate: a + * tunnel alone gets up to HBR3, two get HBR2 (TB_HOST_DP_SHARED_RATE). */ const struct apple_dpin_policy apple_dpin_m4 = { .flow = APPLE_DPIN_CHANGED, .capacity_retry = true, .defer_new_bringup = true, - .host_policy = TB_HOST_DP_NOTIFY | TB_HOST_DP_HBR2_MAX, + .host_policy = TB_HOST_DP_NOTIFY | TB_HOST_DP_SHARED_RATE, .lazy_idle = true, }; diff --git a/drivers/thunderbolt/apple-dpin-test.c b/drivers/thunderbolt/apple-dpin-test.c index c9664ffb5e9a6..0d4891ad74e14 100644 --- a/drivers/thunderbolt/apple-dpin-test.c +++ b/drivers/thunderbolt/apple-dpin-test.c @@ -381,7 +381,7 @@ static void apple_dpin_qualification_profiles(struct kunit *test) /* * M4 (T8132): the M3 flow without DP IN interrupts, a lazy teardown, and - * DP tunnels capped at HBR2. + * DP tunnels sharing one link rate. */ static void apple_dpin_m4_policy(struct kunit *test) { @@ -393,15 +393,15 @@ static void apple_dpin_m4_policy(struct kunit *test) KUNIT_EXPECT_FALSE(test, p->t602x_handshake); KUNIT_EXPECT_TRUE(test, p->defer_new_bringup); KUNIT_EXPECT_TRUE(test, p->lazy_idle); - KUNIT_EXPECT_EQ(test, p->host_policy, TB_HOST_DP_NOTIFY | TB_HOST_DP_HBR2_MAX); + KUNIT_EXPECT_EQ(test, p->host_policy, TB_HOST_DP_NOTIFY | TB_HOST_DP_SHARED_RATE); /* The hooks of the M3 flow: a queue and a display pipeline both. */ KUNIT_EXPECT_EQ(test, apple_dpin_hooks(p, true, true), APPLE_DPIN_CHANGED); KUNIT_EXPECT_EQ(test, apple_dpin_hooks(p, true, false), APPLE_DPIN_DISABLED); KUNIT_EXPECT_EQ(test, apple_dpin_hooks(p, false, true), APPLE_DPIN_DISABLED); - /* No other Apple host is capped. */ - KUNIT_EXPECT_FALSE(test, apple_dpin_m3.host_policy & TB_HOST_DP_HBR2_MAX); - KUNIT_EXPECT_FALSE(test, apple_dpin_m2.host_policy & TB_HOST_DP_HBR2_MAX); - KUNIT_EXPECT_FALSE(test, apple_dpin_m1.host_policy & TB_HOST_DP_HBR2_MAX); + /* No other Apple host shares one rate. */ + KUNIT_EXPECT_FALSE(test, apple_dpin_m3.host_policy & TB_HOST_DP_SHARED_RATE); + KUNIT_EXPECT_FALSE(test, apple_dpin_m2.host_policy & TB_HOST_DP_SHARED_RATE); + KUNIT_EXPECT_FALSE(test, apple_dpin_m1.host_policy & TB_HOST_DP_SHARED_RATE); } static void apple_dpin_all_policy_fields(struct kunit *test) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 95896401571cc..24d01880fdb3e 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "ctl.h" @@ -1111,8 +1112,14 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, enum tb_cfg_space space, u32 offset, u32 length) { - struct tb_cfg_result res = tb_cfg_read_raw(ctl, buffer, route, port, - space, offset, length, ctl->timeout_msec); + struct tb_cfg_result res; + + /* The KUnit tests read adapter registers without hardware */ + KUNIT_STATIC_STUB_REDIRECT(tb_cfg_read, ctl, buffer, route, port, space, offset, + length); + + res = tb_cfg_read_raw(ctl, buffer, route, port, space, offset, length, + ctl->timeout_msec); switch (res.err) { case 0: /* Success */ diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 17020bf90a92a..56ffea80c5981 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -927,6 +927,7 @@ int tb_domain_init(void) ret = bus_register(&tb_bus_type); if (ret) goto err_xdomain; + tb_dp_pm_gate_init(); return 0; @@ -941,6 +942,7 @@ int tb_domain_init(void) void tb_domain_exit(void) { + tb_dp_pm_gate_exit(); bus_unregister(&tb_bus_type); ida_destroy(&tb_domain_ida); tb_nvm_exit(); diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 69aecbd97277c..7201e0c279cc3 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -225,11 +225,12 @@ struct tb_nhi_ops { #define TB_HOST_DP_ADAPTER_QUIRKS BIT(4) #define TB_HOST_DP_INITIAL_BW_GRANT BIT(5) /* - * Both DP IN streams of a host PHY must run at one link rate: DP tunnels - * from the host's DP IN adapters offer at most HBR2, at which two 4K60 - * streams still fit. + * Both DP IN streams of a host PHY must run at one link rate. A DP tunnel + * alone on the host router offers up to HBR3; when more are needed, all of + * them offer HBR2 at most (two 4K60 streams fit), a tunnel up above that + * being set up again at HBR2 first. */ -#define TB_HOST_DP_HBR2_MAX BIT(6) +#define TB_HOST_DP_SHARED_RATE BIT(6) /* * Minimal number of vectors when we use MSI-X. Two for control channel diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 3816b12422064..2f512323e44c9 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include "tb.h" #include "tb_regs.h" @@ -64,6 +67,20 @@ MODULE_PARM_DESC(asym_threshold, * runtime resume * @dp_tunnel_work: Re-pair DP resources after retired DPRX work releases its paths * @dp_tunnel_pending: A pairing request not yet serviced, retained across sleep + * @dp_relink_work: Waits on the freezable queue until no system sleep + * transition is in progress, then queues @dp_relink_run + * @dp_relink_run: The host DP tunnel re-link work proper, on the domain + * queue: after any DPRX or teardown work queued before it + * @dp_relink_deferred: The pairing that needs a host DP tunnel's re-link + * waits for system sleep to end (@dp_tunnel_pending is + * set with it, for the resume and thaw replays) + * @dp_relink_tries: Runtime resume failures of @dp_relink_run in a row + * @dp_relink_due: When the host DP tunnel left alone may be re-linked at a + * higher rate + * @dp_relink_delay: The debounce before that (ms), longer while it flaps + * @dp_relinked_at: When the last such re-link was made + * @dp_host_hold_shared: The next host DP tunnels are set up at HBR2 at + * most: one re-linked at a higher rate did not come up * @groups: Bandwidth groups used in this domain. */ struct tb_cm { @@ -74,6 +91,14 @@ struct tb_cm { struct delayed_work remove_work; struct work_struct dp_tunnel_work; bool dp_tunnel_pending; + struct delayed_work dp_relink_work; + struct work_struct dp_relink_run; + bool dp_relink_deferred; + unsigned int dp_relink_tries; + unsigned long dp_relink_due; + unsigned int dp_relink_delay; + unsigned long dp_relinked_at; + bool dp_host_hold_shared; struct tb_bandwidth_group groups[MAX_GROUPS]; }; @@ -98,6 +123,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port, static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port, int retry, unsigned long delay); static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data); +static void tb_queue_dp_tunnels(struct tb *tb); static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) { @@ -612,12 +638,9 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, * * Return: %0 on success, negative errno otherwise. */ -static int tb_consumed_dp_bandwidth(struct tb *tb, - struct tb_port *src_port, - struct tb_port *dst_port, - struct tb_port *port, - int *consumed_up, - int *consumed_down) +VISIBLE_IF_KUNIT int tb_consumed_dp_bandwidth(struct tb *tb, struct tb_port *src_port, + struct tb_port *dst_port, struct tb_port *port, + int *consumed_up, int *consumed_down) { int group_reserved[MAX_GROUPS + 1] = {}; struct tb_cm *tcm = tb_priv(tb); @@ -1991,6 +2014,7 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) struct tb_port *in = tunnel->src_port; struct tb_port *out = tunnel->dst_port; struct tb *tb = data; + struct tb_cm *tcm = tb_priv(tb); mutex_lock(&tb->lock); @@ -2047,15 +2071,36 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) * loaded or not all DP cables where connected to the * discrete router. */ - tb_tunnel_warn(tunnel, "not active, tearing down\n"); - tb_dp_resource_unavailable(tb, in, - "DPRX negotiation failed"); + if (tb_dp_relink_activation(tunnel->host_relinked_up, false) == + TB_DP_RELINK_NOW) { + /* + * Set up at a higher rate when it was left alone: set + * it up at HBR2 again, once, from the domain queue + * once this DPRX work has released its paths. The + * adapters stay listed. + */ + tb_tunnel_warn(tunnel, "not active at the higher rate, setting it up again at up to %u Mb/s per lane\n", + TB_DP_HOST_RATE_SHARED); + tb_deactivate_and_free_tunnel(tunnel); + tcm->dp_host_hold_shared = true; + tb_recalc_estimated_bandwidth(tb); + tb_queue_dp_tunnels(tb); + } else { + tb_tunnel_warn(tunnel, "not active, tearing down\n"); + tb_dp_resource_unavailable(tb, in, + "DPRX negotiation failed"); + } } mutex_unlock(&tb->lock); } -static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, - struct tb_port *out) +/* + * Set up a DP tunnel from @in to @out. @rate_limit: the highest link rate + * (Mb/s per lane) under TB_HOST_DP_SHARED_RATE. The tunnel if it is up or + * being activated, NULL if not. + */ +static struct tb_tunnel *tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, + struct tb_port *out, u32 rate_limit) { int available_up, available_down, ret, link_nr; struct tb_cm *tcm = tb_priv(tb); @@ -2117,6 +2162,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, tb_port_dbg(out, "could not allocate DP tunnel\n"); goto err_reclaim_usb; } + tunnel->host_rate_limit = rate_limit; list_add_tail(&tunnel->list, &tcm->tunnel_list); @@ -2133,7 +2179,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, list_del(&tunnel->list); goto err_free; } - return; + return tunnel; err_free: tb_tunnel_put(tunnel); @@ -2148,11 +2194,12 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, pm_runtime_put_autosuspend(&out->sw->dev); pm_runtime_mark_last_busy(&in->sw->dev); pm_runtime_put_autosuspend(&in->sw->dev); + return NULL; } /* * The link rate (Mb/s per lane) another DP tunnel from @tunnel's host router - * was given under TB_HOST_DP_HBR2_MAX, 0 if there is none. Called with the + * was given under TB_HOST_DP_SHARED_RATE, 0 if there is none. Called with the * domain lock held, from the tunnel's activation. */ u32 tb_dp_host_shared_rate(const struct tb_tunnel *tunnel) @@ -2167,16 +2214,568 @@ u32 tb_dp_host_shared_rate(const struct tb_tunnel *tunnel) return 0; } +/* + * TB_HOST_DP_SHARED_RATE: the highest link rate (Mb/s per lane) each of + * @tunnels DP tunnels from the host router's DP IN adapters may offer: HBR3 + * for one, HBR2 for more (two 4K60 streams fit at HBR2 x4). + */ +u32 tb_dp_host_rate_for(unsigned int tunnels) +{ + return tunnels > 1 ? TB_DP_HOST_RATE_SHARED : TB_DP_HOST_RATE_ALONE; +} + +/* + * TB_HOST_DP_SHARED_RATE: whether a host DP tunnel up at @rate (Mb/s per + * lane) is set up again at HBR2 when @tunnels are wanted on its host router + * in all. Above HBR2 next to another it is, so that both streams of the PHY + * run at one rate; while system sleep is in progress (@sleeping) only once + * it is over. + */ +enum tb_dp_relink tb_dp_relink_down(u32 rate, unsigned int tunnels, bool sleeping) +{ + if (tunnels < 2 || rate <= TB_DP_HOST_RATE_SHARED) + return TB_DP_RELINK_NONE; + return sleeping ? TB_DP_RELINK_WAIT : TB_DP_RELINK_NOW; +} + +/* + * TB_HOST_DP_SHARED_RATE re-links tear a display's DP tunnel down and set it + * up again. None runs during a system sleep transition: from its PREPARE + * event, before any other PM notifier hears of it, until the transition is + * over, its POST callbacks and the rollback of a refused PREPARE included. + * Every PM notifier call is made with the system sleep lock held, so the + * gate is reopened by a work item that takes that lock after the POST event. + * Admission holds tb_dp_pm_gate for reading while it re-links; the notifier + * takes it for writing, so a re-link in progress is over before any other + * notifier hears of the transition. + */ +static DECLARE_RWSEM(tb_dp_pm_gate); +static struct tb_dp_pm_state tb_dp_pm; + +/* + * PREPARE closes the gate, and drops a reopen asked for by an earlier POST + * that has not run yet (the snapshot device lets go of the system sleep lock + * between its PREPARE and POST). The notifier comes first and refuses + * nothing, so it gets the POST event of every PREPARE it got, also when the + * PREPARE is refused after it: POST asks for the reopen (true). Other + * events: nothing. + */ +bool tb_dp_pm_event(struct tb_dp_pm_state *s, unsigned long event) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + s->sleeping = true; + s->reopen = false; + return false; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + s->reopen = true; + return true; + } + return false; +} + +/* No transition in progress (system sleep lock held): reopen if one ended. */ +void tb_dp_pm_reopen(struct tb_dp_pm_state *s) +{ + if (s->reopen) + s->sleeping = false; + s->reopen = false; +} + +static void tb_dp_pm_reopen_fn(struct work_struct *work) +{ + unsigned int flags = lock_system_sleep(); + + down_write(&tb_dp_pm_gate); + tb_dp_pm_reopen(&tb_dp_pm); + up_write(&tb_dp_pm_gate); + unlock_system_sleep(flags); +} + +static DECLARE_WORK(tb_dp_pm_reopen_work, tb_dp_pm_reopen_fn); + +static int tb_dp_pm_notify(struct notifier_block *nb, unsigned long event, void *unused) +{ + bool reopen; + + down_write(&tb_dp_pm_gate); + reopen = tb_dp_pm_event(&tb_dp_pm, event); + up_write(&tb_dp_pm_gate); + if (reopen) + queue_work(system_unbound_wq, &tb_dp_pm_reopen_work); + return NOTIFY_DONE; +} + +static struct notifier_block tb_dp_pm_nb = { + .notifier_call = tb_dp_pm_notify, + .priority = INT_MAX, +}; + +void tb_dp_pm_gate_init(void) +{ + register_pm_notifier(&tb_dp_pm_nb); +} + +void tb_dp_pm_gate_exit(void) +{ + unregister_pm_notifier(&tb_dp_pm_nb); + flush_work(&tb_dp_pm_reopen_work); +} + +/* + * Whether a host DP tunnel may be re-linked now; if so, tb_dp_relink_done() + * once it is. Never waits for a transition to start or end. + */ +static bool tb_dp_relink_admit(void) +{ + if (!down_read_trylock(&tb_dp_pm_gate)) + return false; + if (!tb_dp_pm.sleeping) + return true; + up_read(&tb_dp_pm_gate); + return false; +} + +static void tb_dp_relink_done(void) +{ + up_read(&tb_dp_pm_gate); +} + +/* No system sleep transition in progress, as far as can be told now. */ +static bool tb_dp_pm_open(void) +{ + if (!tb_dp_relink_admit()) + return false; + tb_dp_relink_done(); + return true; +} + +/* + * The delay (ms) before the re-link work is tried again after its @tries-th + * failure to runtime resume the domain in a row, 0 for not any more: the + * request then waits for the next resume, thaw or plug event. + */ +unsigned int tb_dp_relink_retry_delay(unsigned int tries) +{ + return tries < TB_DP_RELINK_RETRIES ? TB_DP_RELINK_DELAY_MS << tries : 0; +} + +/* + * TB_HOST_DP_SHARED_RATE: as tb_dp_host_rate_for(), HBR2 at most while a + * tunnel re-linked at a higher rate that did not come up is set up again + * (@recovering). + */ +u32 tb_dp_host_rate_after(unsigned int tunnels, bool recovering) +{ + return recovering ? TB_DP_HOST_RATE_SHARED : tb_dp_host_rate_for(tunnels); +} + +/* + * TB_HOST_DP_SHARED_RATE: whether, after a hot unplug took DP tunnels from + * the host router, the one left (@tunnels) up at @rate is marked to be set up + * again at the higher rate both its adapters take (@max_rate, HBR3 at most). + * Nothing is decided while system sleep is in progress (@sleeping). + */ +bool tb_dp_relink_up_wanted(unsigned int tunnels, u32 rate, u32 max_rate, bool sleeping) +{ + return !sleeping && tunnels == 1 && rate < min(max_rate, TB_DP_HOST_RATE_ALONE); +} + +/* + * TB_HOST_DP_SHARED_RATE: whether the host DP tunnel @marked by + * tb_dp_relink_up_wanted() is set up again at the higher rate. Not if + * another one is up or could be paired (@pairable) by now, nor if it got + * that rate already; only after @due, the end of the debounce, and not + * while system sleep is in progress. + */ +enum tb_dp_relink tb_dp_relink_up(bool marked, unsigned int tunnels, u32 rate, u32 max_rate, + bool pairable, bool sleeping, unsigned long now, + unsigned long due) +{ + if (!marked || pairable || !tb_dp_relink_up_wanted(tunnels, rate, max_rate, false)) + return TB_DP_RELINK_NONE; + if (sleeping || time_before(now, due)) + return TB_DP_RELINK_WAIT; + return TB_DP_RELINK_NOW; +} + +/* + * The debounce (ms) before the next re-link to a higher rate: doubled, up + * to TB_DP_RELINK_DELAY_MAX_MS, each time a second DP tunnel takes the rate + * back down soon after such a re-link (@flapping), the base one otherwise. + */ +unsigned int tb_dp_relink_backoff(unsigned int delay_ms, bool flapping) +{ + if (!flapping) + return TB_DP_RELINK_DELAY_MS; + return min(delay_ms * 2, TB_DP_RELINK_DELAY_MAX_MS); +} + +/* + * A host DP tunnel's activation ended, @active or not: NOW if it is set up + * at HBR2 again because it was re-linked at a higher rate (@relinked_up) + * and did not come up. Only once: that tunnel is not re-linked up. + */ +enum tb_dp_relink tb_dp_relink_activation(bool relinked_up, bool active) +{ + return relinked_up && !active ? TB_DP_RELINK_NOW : TB_DP_RELINK_NONE; +} + +/* + * TB_HOST_DP_SHARED_RATE: another host DP tunnel is wanted while @tunnel, + * marked by tb_dp_host_tunnel_gone(), waits for its re-link at a higher + * rate. The re-link is dropped and @tunnel stays capped at HBR2, the rate + * the new one takes next to it, so that it is counted at HBR2 when the + * bandwidth for the new one is worked out. True if a re-link was dropped. + */ +bool tb_dp_host_drop_relink_up(struct tb_tunnel *tunnel) +{ + if (!tunnel->host_relink) + return false; + tunnel->host_relink = false; + tunnel->host_rate_limit = TB_DP_HOST_RATE_SHARED; + tunnel->max_rate = min(tunnel->max_rate, TB_DP_HOST_RATE_SHARED); + return true; +} + +static bool tb_dp_host_shares_rate(const struct tb *tb) +{ + return tb->nhi && (tb->nhi->host_dp_policy & TB_HOST_DP_SHARED_RATE); +} + +/* A DP tunnel from a DP IN adapter of the (Apple) host router. */ +static bool tb_tunnel_is_host_dp(const struct tb_tunnel *tunnel) +{ + return tb_tunnel_is_dp(tunnel) && tb_port_is_apple_host_dpin(tunnel->src_port); +} + +/* The DP tunnels from the host router's DP IN adapters; @last: the last one. */ +static unsigned int tb_dp_host_tunnels(struct tb *tb, struct tb_tunnel **last) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel; + unsigned int n = 0; + + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (!tb_tunnel_is_host_dp(tunnel)) + continue; + n++; + if (last) + *last = tunnel; + } + return n; +} + +/* + * How many more DP tunnels from the host router's DP IN adapters pairing + * can set up now: its free DP IN adapters against the free DP OUT adapters + * of the other routers (see tb_find_dp_out()). + */ +static unsigned int tb_dp_host_free_pairs(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + unsigned int ins = 0, outs = 0; + struct tb_port *port; + + list_for_each_entry(port, &tcm->dp_resources, list) { + if (tb_port_is_dpin(port)) { + if (tb_port_is_apple_host_dpin(port) && !tb_port_is_enabled(port)) + ins++; + } else if (tb_port_is_dpout(port) && tb_route(port->sw) && + !tb_port_is_enabled(port)) { + outs++; + } + } + return min(ins, outs); +} + +/* A new DP tunnel comes: none is re-linked to a higher rate any more. */ +static void tb_dp_host_clear_relink(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel; + + list_for_each_entry(tunnel, &tcm->tunnel_list, list) + if (tb_tunnel_is_host_dp(tunnel)) + tunnel->host_relink = false; +} + +/* + * Another host DP tunnel is wanted: a tunnel waiting for its re-link at a + * higher rate is held at HBR2 instead (tb_dp_host_drop_relink_up()) and its + * estimated bandwidth worked out again, before any bandwidth is given to + * the new one. The debounce is stopped unless deferred pairing needs the + * re-link work. Domain lock held. + */ +static void tb_dp_host_cancel_relink_up(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel; + bool dropped = false; + + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (!tb_tunnel_is_host_dp(tunnel) || !tb_dp_host_drop_relink_up(tunnel)) + continue; + tb_tunnel_info(tunnel, "another DP tunnel is wanted: kept at up to %u Mb/s per lane\n", + TB_DP_HOST_RATE_SHARED); + dropped = true; + } + if (!dropped) + return; + if (!tcm->dp_relink_deferred && cancel_delayed_work(&tcm->dp_relink_work)) + tb_domain_put(tb); + tb_recalc_estimated_bandwidth(tb); +} + +/* + * Run the re-link work (dp_relink_run) @delay_ms from now, and only once no + * system sleep transition is in progress. One domain reference for each + * queued execution of either work, as for dp_tunnel_work. Domain lock held. + */ +static void tb_dp_relink_arm(struct tb *tb, unsigned int delay_ms) +{ + struct tb_cm *tcm = tb_priv(tb); + + lockdep_assert_held(&tb->lock); + if (tcm->stopping) + return; + tb_domain_get(tb); + if (mod_delayed_work(system_freezable_wq, &tcm->dp_relink_work, + msecs_to_jiffies(delay_ms))) + tb_domain_put(tb); +} + +/* + * Tear @tunnel down and set it up again between the same adapters, at + * @rate_limit (Mb/s per lane) at most, because of @why. Its display sees an + * unplug and a plug. Only on the domain queue, so that the DPRX work of + * @tunnel is not running (the queue is ordered) and releases its paths when + * canceled; with the domain lock held and the re-link admitted + * (tb_dp_relink_admit()). The new tunnel, NULL if there is none. + */ +static struct tb_tunnel *tb_dp_host_relink(struct tb *tb, struct tb_tunnel *tunnel, + u32 rate_limit, const char *why) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *relinked; + struct tb_port *in = tunnel->src_port; + struct tb_port *out = tunnel->dst_port; + + tb_tunnel_info(tunnel, "%s: re-linking at up to %u Mb/s per lane (was %u)\n", + why, rate_limit, tunnel->host_rate); + tb_deactivate_and_free_tunnel(tunnel); + tb_recalc_estimated_bandwidth(tb); + relinked = tb_tunnel_one_dp(tb, in, out, rate_limit); + if (relinked) + return relinked; + /* Not at the higher rate: at the shared one, as before. */ + if (rate_limit > TB_DP_HOST_RATE_SHARED) { + relinked = tb_tunnel_one_dp(tb, in, out, TB_DP_HOST_RATE_SHARED); + if (relinked) { + tb_port_warn(in, "DP tunnel set up again at up to %u Mb/s per lane\n", + TB_DP_HOST_RATE_SHARED); + return relinked; + } + tcm->dp_host_hold_shared = true; + } + /* The adapters stay listed: pairing tries again, at the next event too. */ + tb_port_warn(in, "DP tunnel not set up again, pairing again\n"); + tb_queue_dp_tunnels(tb); + return NULL; +} + +/* + * TB_HOST_DP_SHARED_RATE: the link rate limit (Mb/s per lane) for the DP + * tunnels pairing sets up now from the host router's DP IN adapters: HBR3 + * when one tunnel will be up, HBR2 when more will. A tunnel already up + * above HBR2 when more are wanted is set up again at HBR2 first, between + * the same adapters, so that the streams of the PHY share one rate. Not + * while system sleep is in progress: then 0, and the host router's DP IN + * adapters are paired once it is over (dp_relink_work), or at the next + * resume or thaw. 0 without the policy. Domain lock held. + */ +VISIBLE_IF_KUNIT u32 tb_dp_host_rate_limit(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel, *above; + bool admitted, recovering = false; + enum tb_dp_relink action; + unsigned int up, want; + unsigned long calm; + + if (!tb_dp_host_shares_rate(tb)) + return 0; + up = tb_dp_host_tunnels(tb, NULL); + want = up + tb_dp_host_free_pairs(tb); + /* + * Another one comes: the one alone is not re-linked to a higher rate, + * and counts at HBR2 for the new one's bandwidth. + */ + if (want > up) { + tb_dp_host_cancel_relink_up(tb); + recovering = tcm->dp_host_hold_shared; + tcm->dp_host_hold_shared = false; + } + + /* Each round sets one tunnel up again at HBR2 at most. */ + for (;;) { + above = NULL; + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (tb_tunnel_is_host_dp(tunnel) && + tunnel->host_rate > TB_DP_HOST_RATE_SHARED) { + above = tunnel; + break; + } + } + if (!above) + break; + + admitted = tb_dp_relink_admit(); + action = tb_dp_relink_down(above->host_rate, want, !admitted); + if (action == TB_DP_RELINK_NOW) { + /* Soon after a re-link up: back off the next one. */ + calm = tcm->dp_relinked_at + msecs_to_jiffies(TB_DP_RELINK_CALM_MS); + tcm->dp_relink_delay = tb_dp_relink_backoff(tcm->dp_relink_delay, + time_before(jiffies, calm)); + tb_dp_host_relink(tb, above, TB_DP_HOST_RATE_SHARED, + "another DP tunnel is wanted"); + } + if (admitted) { + tb_dp_relink_done(); + tcm->dp_relink_deferred = false; + } + if (action == TB_DP_RELINK_NONE) + break; + if (action == TB_DP_RELINK_WAIT) { + if (!tcm->dp_relink_deferred) + tb_tunnel_info(above, "another DP tunnel is wanted during system sleep, re-linking after it\n"); + /* Replayed by dp_relink_work, and at resume or thaw. */ + tcm->dp_relink_deferred = true; + tcm->dp_tunnel_pending = true; + tcm->dp_host_hold_shared |= recovering; + tb_dp_relink_arm(tb, 0); + return 0; + } + } + return tb_dp_host_rate_after(want, recovering); +} + +/* + * TB_HOST_DP_SHARED_RATE: a hot unplug took DP tunnels from the host + * router's DP IN adapters (@before were up). The one left alone below the + * rate it could get is set up again at that rate once the debounce + * (dp_relink_delay) is over, unless another one comes by then, so that an + * unplug and replug or a retraining does not cause a re-link. Until it is + * set up again, it keeps its HBR2 cap and is counted at HBR2. Nothing is + * marked during a system sleep transition. Domain lock held. + */ +VISIBLE_IF_KUNIT void tb_dp_host_tunnel_gone(struct tb *tb, unsigned int before) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel = NULL; + unsigned int up; + + if (!tb_dp_host_shares_rate(tb)) + return; + up = tb_dp_host_tunnels(tb, &tunnel); + if (!tunnel || up >= before || + !tb_dp_relink_up_wanted(up, tunnel->host_rate, tunnel->host_max_rate, + !tb_dp_pm_open())) + return; + + tunnel->host_relink = true; + tcm->dp_relink_due = jiffies + msecs_to_jiffies(tcm->dp_relink_delay); + tb_tunnel_info(tunnel, "alone at %u Mb/s per lane: re-linking at up to %u in %u ms if no other DP tunnel comes\n", + tunnel->host_rate, min(tunnel->host_max_rate, TB_DP_HOST_RATE_ALONE), + tcm->dp_relink_delay); + tb_dp_relink_arm(tb, tcm->dp_relink_delay); +} + +/* A host DP tunnel waits for its re-link to a higher rate. */ +static bool tb_dp_host_relink_marked(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel; + + list_for_each_entry(tunnel, &tcm->tunnel_list, list) + if (tb_tunnel_is_host_dp(tunnel) && tunnel->host_relink) + return true; + return false; +} + +/* + * The debounced part of tb_dp_host_tunnel_gone(), from dp_relink_run on the + * domain queue. Domain lock held. + */ +static void tb_dp_host_relink_up(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_tunnel *tunnel = NULL, *relinked; + enum tb_dp_relink action; + unsigned int up; + bool admitted; + long left; + + if (!tb_dp_host_relink_marked(tb)) + return; + up = tb_dp_host_tunnels(tb, &tunnel); + + admitted = tb_dp_relink_admit(); + action = tb_dp_relink_up(tunnel->host_relink, up, tunnel->host_rate, + tunnel->host_max_rate, tb_dp_host_free_pairs(tb) > 0, + !admitted, jiffies, tcm->dp_relink_due); + switch (action) { + case TB_DP_RELINK_NONE: + tb_dbg(tb, "DP tunnel re-link to a higher rate not needed any more\n"); + tb_dp_host_clear_relink(tb); + break; + case TB_DP_RELINK_WAIT: + left = (long)(tcm->dp_relink_due - jiffies); + tb_dp_relink_arm(tb, left > 0 ? jiffies_to_msecs(left) : TB_DP_RELINK_POLL_MS); + break; + case TB_DP_RELINK_NOW: + tunnel->host_relink = false; + tcm->dp_relinked_at = jiffies; + relinked = tb_dp_host_relink(tb, tunnel, TB_DP_HOST_RATE_ALONE, + "alone on the host router"); + /* Set up at HBR2 again if it does not come up at this rate. */ + if (relinked && relinked->host_rate_limit > TB_DP_HOST_RATE_SHARED) + relinked->host_relinked_up = true; + break; + } + if (admitted) + tb_dp_relink_done(); +} + +/* Replays a re-link that waited, at resume and runtime resume. Domain lock held. */ +static void tb_dp_relink_replay(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + + if (tcm->dp_relink_deferred || tb_dp_host_relink_marked(tb)) + tb_dp_relink_arm(tb, 0); +} + static void tb_tunnel_dp(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); struct tb_port *port, *in, *out; + bool host_waits; + u32 rate_limit; if (!tb_acpi_may_tunnel_dp()) { tb_dbg(tb, "DP tunneling disabled, not creating tunnel\n"); return; } + /* Under TB_HOST_DP_SHARED_RATE: may set a tunnel up again first. */ + rate_limit = tb_dp_host_rate_limit(tb); + host_waits = tb_dp_host_shares_rate(tb) && !rate_limit; + /* * Find pair of inactive DP IN and DP OUT adapters and then * establish a DP tunnel between them. @@ -2197,9 +2796,14 @@ static void tb_tunnel_dp(struct tb *tb) in = port; tb_port_dbg(in, "DP IN available\n"); + if (host_waits && tb_port_is_apple_host_dpin(in)) { + tb_port_dbg(in, "waiting for system sleep to end\n"); + continue; + } + out = tb_find_dp_out(tb, port); if (out) - tb_tunnel_one_dp(tb, in, out); + tb_tunnel_one_dp(tb, in, out, rate_limit); else tb_port_dbg(in, "no suitable DP OUT adapter available, not tunneling\n"); } @@ -2316,6 +2920,79 @@ static void tb_dp_tunnel_work(struct work_struct *work) tb_domain_put(tb); } +/* + * The timer and gate of the re-link work, on the freezable queue: it does + * not run while tasks are frozen, and hands dp_relink_run to the domain + * queue only while no system sleep transition is in progress. + */ +static void tb_dp_relink_work(struct work_struct *work) +{ + struct tb_cm *tcm = container_of(to_delayed_work(work), struct tb_cm, + dp_relink_work); + struct tb *tb = tcm_to_tb(tcm); + + mutex_lock(&tb->lock); + if (!tcm->stopping) { + if (!tb_dp_pm_open()) { + tb_dp_relink_arm(tb, TB_DP_RELINK_POLL_MS); + } else { + tb_domain_get(tb); + if (!queue_work(tb->wq, &tcm->dp_relink_run)) + tb_domain_put(tb); + } + } + mutex_unlock(&tb->lock); + tb_domain_put(tb); +} + +/* + * Pairing that waited for system sleep to end, see tb_dp_host_rate_limit(), + * and the debounced re-link of tb_dp_host_tunnel_gone(). On the domain + * queue: behind DPRX and teardown work queued before it, so a tunnel torn + * down there has released its paths. + */ +static void tb_dp_relink_run(struct work_struct *work) +{ + struct tb_cm *tcm = container_of(work, struct tb_cm, dp_relink_run); + struct tb *tb = tcm_to_tb(tcm); + unsigned int delay; + int ret; + + /* Runtime resume can acquire the domain lock, so do it first. */ + ret = pm_runtime_resume_and_get(&tb->dev); + + mutex_lock(&tb->lock); + if (tcm->stopping || !tb->root_switch) + goto unlock; + if (ret < 0) { + /* A few more tries; then the next resume, thaw or plug event. */ + delay = tb_dp_relink_retry_delay(tcm->dp_relink_tries++); + if (delay) + tb_dp_relink_arm(tb, delay); + else + tb_dbg(tb, "DP re-link waits for the next event: runtime resume failed: %d\n", + ret); + goto unlock; + } + tcm->dp_relink_tries = 0; + if (tcm->hotplug_active && tcm->dp_relink_deferred) { + tb_dbg(tb, "system sleep over, pairing DP resources\n"); + tcm->dp_tunnel_pending = false; + /* Checks the gate again, and defers again if it is closed. */ + tb_tunnel_dp(tb); + } + if (tcm->hotplug_active) + tb_dp_host_relink_up(tb); +unlock: + mutex_unlock(&tb->lock); + + if (ret >= 0) { + pm_runtime_mark_last_busy(&tb->dev); + pm_runtime_put_autosuspend(&tb->dev); + } + tb_domain_put(tb); +} + static void tb_queue_dp_tunnels(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); @@ -2614,6 +3291,8 @@ static void tb_handle_hotplug(struct work_struct *work) pm_runtime_get_sync(&sw->dev); if (ev->unplug) { + unsigned int dp_up = tb_dp_host_tunnels(tb, NULL); + tb_retimer_remove_all(port); if (tb_port_has_remote(port)) { @@ -2632,6 +3311,7 @@ static void tb_handle_hotplug(struct work_struct *work) /* Maybe we can create another DP tunnel */ tb_recalc_estimated_bandwidth(tb); tb_tunnel_dp(tb); + tb_dp_host_tunnel_gone(tb, dp_up); } else if (port->xdomain) { struct tb_xdomain *xd = tb_xdomain_get(port->xdomain); @@ -2651,6 +3331,9 @@ static void tb_handle_hotplug(struct work_struct *work) tb_port_unconfigure_xdomain(port); } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) { tb_dp_resource_unavailable(tb, port, "adapter unplug"); + /* A display went: the DP tunnel left may take a higher rate. */ + if (tb_port_is_dpout(port)) + tb_dp_host_tunnel_gone(tb, dp_up); } else if (!port->port) { tb_sw_dbg(sw, "xHCI disconnect request\n"); tb_switch_xhci_disconnect(sw); @@ -3110,6 +3793,10 @@ static void tb_stop(struct tb *tb) tcm->stopping = true; tcm->hotplug_active = false; cancel_delayed_work(&tcm->remove_work); + if (cancel_delayed_work(&tcm->dp_relink_work)) + tb_domain_put(tb); + if (cancel_work(&tcm->dp_relink_run)) + tb_domain_put(tb); list_for_each_entry(tunnel, &tcm->tunnel_list, list) { if (tb_tunnel_is_dp(tunnel)) tb_dp_tunnel_cancel_dprx(tunnel); @@ -3169,6 +3856,11 @@ static void tb_deinit(struct tb *tb) /* Cancel all the release bandwidth workers */ for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) cancel_delayed_work_sync(&tcm->groups[i].release_work); + /* Stopped: nothing queues them again. */ + if (cancel_delayed_work_sync(&tcm->dp_relink_work)) + tb_domain_put(tb); + if (cancel_work_sync(&tcm->dp_relink_run)) + tb_domain_put(tb); } static int tb_scan_finalize_switch(struct device *dev, void *data) @@ -3540,6 +4232,7 @@ static void tb_complete(struct tb *tb) /* A noirq replay may have failed while runtime PM was disabled. */ if (tcm->dp_tunnel_pending) tb_queue_dp_tunnels(tb); + tb_dp_relink_replay(tb); } static int tb_runtime_suspend(struct tb *tb) @@ -3596,6 +4289,7 @@ static int tb_runtime_resume(struct tb *tb) tcm->hotplug_active = true; if (tcm->dp_tunnel_pending) tb_queue_dp_tunnels(tb); + tb_dp_relink_replay(tb); mutex_unlock(&tb->lock); /* @@ -3694,12 +4388,24 @@ static bool tb_apple_add_links(struct tb_nhi *nhi) return ret; } +static void tb_cm_init(struct tb_cm *tcm) +{ + INIT_LIST_HEAD(&tcm->tunnel_list); + INIT_LIST_HEAD(&tcm->dp_resources); + INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work); + INIT_WORK(&tcm->dp_tunnel_work, tb_dp_tunnel_work); + INIT_DELAYED_WORK(&tcm->dp_relink_work, tb_dp_relink_work); + INIT_WORK(&tcm->dp_relink_run, tb_dp_relink_run); + tcm->dp_relink_delay = TB_DP_RELINK_DELAY_MS; + tcm->dp_relinked_at = jiffies - msecs_to_jiffies(TB_DP_RELINK_CALM_MS); + tb_init_bandwidth_groups(tcm); +} + struct tb *tb_probe(struct tb_nhi *nhi) { - struct tb_cm *tcm; struct tb *tb; - tb = tb_domain_alloc(nhi, TB_TIMEOUT, sizeof(*tcm)); + tb = tb_domain_alloc(nhi, TB_TIMEOUT, sizeof(struct tb_cm)); if (!tb) return NULL; @@ -3710,12 +4416,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) tb->cm_ops = &tb_cm_ops; - tcm = tb_priv(tb); - INIT_LIST_HEAD(&tcm->tunnel_list); - INIT_LIST_HEAD(&tcm->dp_resources); - INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work); - INIT_WORK(&tcm->dp_tunnel_work, tb_dp_tunnel_work); - tb_init_bandwidth_groups(tcm); + tb_cm_init(tb_priv(tb)); tb_dbg(tb, "using software connection manager\n"); @@ -3730,3 +4431,60 @@ struct tb *tb_probe(struct tb_nhi *nhi) return tb; } EXPORT_SYMBOL_FOR_MODULES(tb_probe, "thunderbolt_apple"); + +#if IS_ENABLED(CONFIG_USB4_KUNIT_TEST) +/* + * For the KUnit tests: the software connection manager of a domain the test + * sets up itself, with no hardware behind it. The domain is allocated with + * tb_test_cm_size() bytes of private data. + */ +size_t tb_test_cm_size(void) +{ + return sizeof(struct tb_cm); +} + +void tb_test_cm_init(struct tb *tb) +{ + tb_cm_init(tb_priv(tb)); +} + +struct list_head *tb_test_cm_tunnels(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + + return &tcm->tunnel_list; +} + +struct list_head *tb_test_cm_dp_resources(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + + return &tcm->dp_resources; +} + +void tb_test_cm_set_relink_deferred(struct tb *tb, bool deferred) +{ + struct tb_cm *tcm = tb_priv(tb); + + tcm->dp_relink_deferred = deferred; +} + +/* The re-link work's timer is armed. */ +bool tb_test_cm_relink_armed(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + + return delayed_work_pending(&tcm->dp_relink_work); +} + +/* Stop the re-link work as tb_deinit() does, dropping its domain references. */ +void tb_test_cm_stop(struct tb *tb) +{ + struct tb_cm *tcm = tb_priv(tb); + + if (cancel_delayed_work_sync(&tcm->dp_relink_work)) + tb_domain_put(tb); + if (cancel_work_sync(&tcm->dp_relink_run)) + tb_domain_put(tb); +} +#endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 8bcba8e8434d5..841d1ccb27042 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -764,6 +764,22 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, struct tb *icm_probe(struct tb_nhi *nhi); struct tb *tb_probe(struct tb_nhi *nhi); +#if IS_ENABLED(CONFIG_KUNIT) +int tb_consumed_dp_bandwidth(struct tb *tb, struct tb_port *src_port, + struct tb_port *dst_port, struct tb_port *port, + int *consumed_up, int *consumed_down); +u32 tb_dp_host_rate_limit(struct tb *tb); +void tb_dp_host_tunnel_gone(struct tb *tb, unsigned int before); +#endif +#if IS_ENABLED(CONFIG_USB4_KUNIT_TEST) +size_t tb_test_cm_size(void); +void tb_test_cm_init(struct tb *tb); +struct list_head *tb_test_cm_tunnels(struct tb *tb); +struct list_head *tb_test_cm_dp_resources(struct tb *tb); +void tb_test_cm_set_relink_deferred(struct tb *tb, bool deferred); +bool tb_test_cm_relink_armed(struct tb *tb); +void tb_test_cm_stop(struct tb *tb); +#endif extern const struct device_type tb_domain_type; extern const struct device_type tb_retimer_type; @@ -772,6 +788,8 @@ extern const struct device_type usb4_port_device_type; int tb_domain_init(void); void tb_domain_exit(void); +void tb_dp_pm_gate_init(void); +void tb_dp_pm_gate_exit(void); int tb_xdomain_init(void); void tb_xdomain_exit(void); diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 745489aef1941..f8418132064af 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -7,8 +7,10 @@ */ #include +#include #include #include +#include #include "tb.h" #include "tunnel.h" @@ -3605,8 +3607,8 @@ static void tb_test_apple_right_dpin_wiring(struct kunit *test) } /* - * TB_HOST_DP_HBR2_MAX: a second DP tunnel keeps the rate of the first and - * gives up lanes instead; alone, lanes go before the rate. + * TB_HOST_DP_SHARED_RATE: a second DP tunnel keeps the rate of the first and + * gives up lanes instead; alone at HBR2, lanes go before the rate. */ static void tb_test_dp_reduce_bandwidth_shared(struct kunit *test) { @@ -3646,9 +3648,690 @@ static void tb_test_dp_reduce_bandwidth_shared(struct kunit *test) &rate, &lanes), -ENOSR); } +/* + * TB_HOST_DP_SHARED_RATE: one DP tunnel on the host router may offer HBR3, + * more offer HBR2. Alone, a reduction keeps the most bandwidth; held at + * HBR2 or next to another tunnel, the lanes go first. + */ +static void tb_test_dp_shared_rate_single(struct kunit *test) +{ + u32 rate, lanes; + + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_for(0), 8100); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_for(1), 8100); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_for(2), 5400); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_for(3), 5400); + + /* Alone, the dock's 34650 Mb/s: HBR3 x4 (25920 Mb/s) */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(34650, 8100, 4, 8100, 4, 0, 8100, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 8100); + KUNIT_EXPECT_EQ(test, lanes, 4); + /* Alone, 20000 Mb/s: HBR2 x4 (17280), not HBR3 x2 (12960, too little for 4K60) */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(20000, 8100, 4, 8100, 4, 0, 8100, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 4); + /* Alone, a sink side that takes HBR2 at most: HBR2 x4 */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(34650, 8100, 4, 5400, 4, 0, 8100, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 4); + /* Held at HBR2 for a second tunnel, 10000 Mb/s: HBR2 x2, lanes first */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(10000, 5400, 4, 5400, 4, 0, 5400, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 2); +} + +/* + * TB_HOST_DP_SHARED_RATE: with two DP tunnels the second takes the first + * one's rate, lanes first; a first one up above HBR2 is set up again at HBR2. + */ +static void tb_test_dp_shared_rate_second(struct kunit *test) +{ + u32 rate, lanes; + + /* A second one wanted next to one at HBR3: re-link it now */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(8100, 2, false), TB_DP_RELINK_NOW); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(8100, 3, false), TB_DP_RELINK_NOW); + /* Alone: kept */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(8100, 1, false), TB_DP_RELINK_NONE); + /* At HBR2 or below: kept, the second one takes its rate */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(5400, 2, false), TB_DP_RELINK_NONE); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(2700, 2, false), TB_DP_RELINK_NONE); + + /* + * The first one, held at HBR2 between adapters that take HBR3, is + * counted at HBR2 whatever its DP IN adapter reports: 34650 - 17280 + * left for the second, not 34650 - 25920 (HBR2 x2 at most) + */ + KUNIT_EXPECT_EQ(test, tb_dp_host_counted_bandwidth(5400, 8100, 4), 17280); + KUNIT_EXPECT_EQ(test, tb_dp_host_counted_bandwidth(5400, 5400, 2), 8640); + KUNIT_EXPECT_EQ(test, tb_dp_host_counted_bandwidth(8100, 8100, 4), 25920); + KUNIT_EXPECT_EQ(test, tb_dp_host_counted_bandwidth(0, 8100, 4), 25920); + + /* Two HBR2 x4 tunnels fit the dock: 34650 - 17280 left for the second */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(17370, 5400, 4, 5400, 4, 5400, 5400, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 4); + /* Less room: the second gives up lanes, never its rate */ + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(12000, 5400, 4, 8100, 4, 5400, 5400, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 2); + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(4000, 5400, 4, 5400, 4, 5400, 5400, + &rate, &lanes), -ENOSR); +} + +/* + * TB_HOST_DP_SHARED_RATE: when the other DP tunnel goes, the one left at + * HBR2 is re-linked at HBR3 once the debounce is over, unless another one + * comes by then; once per unplug, and the debounce backs off while it flaps. + */ +static void tb_test_dp_shared_rate_relink_up(struct kunit *test) +{ + const unsigned long due = 1000; + + /* Alone at HBR2, HBR3 possible: marked */ + KUNIT_EXPECT_TRUE(test, tb_dp_relink_up_wanted(1, 5400, 8100, false)); + /* Still two, none left, already at its best, or an HBR2-only sink side: not */ + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(2, 5400, 8100, false)); + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(0, 0, 0, false)); + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(1, 8100, 8100, false)); + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(1, 5400, 5400, false)); + + /* Within the debounce: wait; after it: re-link */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 1, 5400, 8100, false, false, due - 1, due), + TB_DP_RELINK_WAIT); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 1, 5400, 8100, false, false, due, due), + TB_DP_RELINK_NOW); + /* Not marked (the re-linked tunnel itself): once only */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(false, 1, 5400, 8100, false, false, due, due), + TB_DP_RELINK_NONE); + /* Another one came, or could be paired: dropped */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 2, 5400, 8100, false, false, due, due), + TB_DP_RELINK_NONE); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 1, 5400, 8100, true, false, due, due), + TB_DP_RELINK_NONE); + /* At HBR3 by now: nothing to do */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 1, 8100, 8100, false, false, due, due), + TB_DP_RELINK_NONE); + + /* Back-off: doubled while it flaps, at most 40 s; reset when calm */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_backoff(2500, false), 2500); + KUNIT_EXPECT_EQ(test, tb_dp_relink_backoff(2500, true), 5000); + KUNIT_EXPECT_EQ(test, tb_dp_relink_backoff(20000, true), 40000); + KUNIT_EXPECT_EQ(test, tb_dp_relink_backoff(40000, true), 40000); + KUNIT_EXPECT_EQ(test, tb_dp_relink_backoff(40000, false), 2500); +} + +/* + * TB_HOST_DP_SHARED_RATE: a tunnel re-linked at HBR3 that does not come up + * is set up again at HBR2, once and without a new re-link up; any other + * tunnel that does not come up is torn down as before. + */ +static void tb_test_dp_shared_rate_recover(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, tb_dp_relink_activation(true, false), TB_DP_RELINK_NOW); + KUNIT_EXPECT_EQ(test, tb_dp_relink_activation(true, true), TB_DP_RELINK_NONE); + KUNIT_EXPECT_EQ(test, tb_dp_relink_activation(false, false), TB_DP_RELINK_NONE); + /* The recovery: HBR2 at most even alone; afterwards HBR3 alone again */ + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_after(1, true), 5400); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_after(1, false), 8100); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_after(2, false), 5400); + /* The tunnel set up so (no re-link mark) is never re-linked up again */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(false, 1, 5400, 8100, false, false, 1000, 1000), + TB_DP_RELINK_NONE); +} + +/* + * TB_HOST_DP_SHARED_RATE: no re-link while system sleep is in progress, from + * the PREPARE event until the transition is over, also when it is refused. + */ +static void tb_test_dp_shared_rate_sleep(struct kunit *test) +{ + struct tb_dp_pm_state s = {}; + + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(8100, 2, true), TB_DP_RELINK_WAIT); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(5400, 2, true), TB_DP_RELINK_NONE); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(8100, 1, true), TB_DP_RELINK_NONE); + + /* Suspend: closed at PREPARE, open only once the transition is over */ + KUNIT_EXPECT_FALSE(test, tb_dp_pm_event(&s, PM_SUSPEND_PREPARE)); + KUNIT_EXPECT_TRUE(test, s.sleeping); + KUNIT_EXPECT_TRUE(test, tb_dp_pm_event(&s, PM_POST_SUSPEND)); + KUNIT_EXPECT_TRUE(test, s.sleeping); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + /* Refused: still closed during the POST replay, until the reopen */ + tb_dp_pm_event(&s, PM_SUSPEND_PREPARE); + KUNIT_EXPECT_TRUE(test, tb_dp_pm_event(&s, PM_POST_SUSPEND)); + KUNIT_EXPECT_TRUE(test, s.sleeping); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + /* No reopen without a POST event: closed until it comes */ + tb_dp_pm_event(&s, PM_SUSPEND_PREPARE); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_TRUE(test, s.sleeping); + tb_dp_pm_event(&s, PM_POST_SUSPEND); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + /* + * The reopen asked for by one transition runs after the next one began + * (the snapshot device drops the lock in between): closed until that + * transition's POST and the reopen after it + */ + tb_dp_pm_event(&s, PM_HIBERNATION_PREPARE); + tb_dp_pm_event(&s, PM_POST_HIBERNATION); + tb_dp_pm_event(&s, PM_HIBERNATION_PREPARE); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_TRUE(test, s.sleeping); + KUNIT_EXPECT_TRUE(test, tb_dp_pm_event(&s, PM_POST_HIBERNATION)); + KUNIT_EXPECT_TRUE(test, s.sleeping); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + /* Two transitions before the reopen runs: one reopen, at the end */ + tb_dp_pm_event(&s, PM_HIBERNATION_PREPARE); + tb_dp_pm_event(&s, PM_POST_HIBERNATION); + tb_dp_pm_event(&s, PM_RESTORE_PREPARE); + tb_dp_pm_event(&s, PM_POST_RESTORE); + KUNIT_EXPECT_TRUE(test, s.sleeping); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + tb_dp_pm_reopen(&s); + KUNIT_EXPECT_FALSE(test, s.sleeping); + /* Other events change nothing */ + tb_dp_pm_event(&s, PM_SUSPEND_PREPARE); + KUNIT_EXPECT_FALSE(test, tb_dp_pm_event(&s, 0)); + KUNIT_EXPECT_TRUE(test, s.sleeping); + + /* Tunnels going during the sleep mark nothing */ + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(1, 5400, 8100, true)); + /* A re-link to HBR3 due during the sleep waits for its end */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(true, 1, 5400, 8100, false, true, 1001, 1000), + TB_DP_RELINK_WAIT); +} + +/* + * TB_HOST_DP_SHARED_RATE: re-link work that cannot runtime resume the domain + * tries again, a few times and later each time; then the request waits for + * the next resume, thaw or plug event. + */ +static void tb_test_dp_shared_rate_retry(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, tb_dp_relink_retry_delay(0), 2500); + KUNIT_EXPECT_EQ(test, tb_dp_relink_retry_delay(1), 5000); + KUNIT_EXPECT_EQ(test, tb_dp_relink_retry_delay(4), 40000); + KUNIT_EXPECT_EQ(test, tb_dp_relink_retry_delay(5), 0); + KUNIT_EXPECT_EQ(test, tb_dp_relink_retry_delay(100), 0); +} + +/* The dock's link: what is left there for DP tunnels (Mb/s) */ +#define TB_TEST_DOCK_DP_BW 34650 + +/* + * Set @t up as pairing and the capabilities exchange do under + * TB_HOST_DP_SHARED_RATE, between adapters that take HBR3 x4: capped at + * @rate_limit, next to a tunnel at @shared_rate (0: alone), with @room Mb/s + * left on the link. The lanes it takes in @lanes. + */ +static void tb_test_host_dp_up(struct kunit *test, struct tb_tunnel *t, u32 rate_limit, + u32 shared_rate, int room, u32 *lanes) +{ + u32 rate; + int ret; + + memset(t, 0, sizeof(*t)); + t->host_rate_limit = rate_limit; + t->max_rate = rate_limit; + t->host_max_rate = TB_DP_HOST_RATE_ALONE; + ret = tb_dp_host_reduce_bandwidth(room, rate_limit, 4, rate_limit, 4, shared_rate, + rate_limit, &rate, lanes); + KUNIT_ASSERT_EQ(test, ret, 0); + t->max_rate = min(t->max_rate, rate); + t->host_rate = rate; +} + +/* + * What @t, up at @lanes, leaves on the dock's link for another DP tunnel, + * its DP IN adapter reporting the rate both adapters take (HBR3). + */ +static int tb_test_host_dp_room(const struct tb_tunnel *t, u32 lanes) +{ + return TB_TEST_DOCK_DP_BW - + tb_dp_host_counted_bandwidth(t->max_rate, TB_DP_HOST_RATE_ALONE, lanes); +} + +/* + * A at HBR2 x4 next to B: B is wanted while A is up at HBR3, so A is set up + * again at HBR2 first, alone on the link, then B takes its rate. + */ +static void tb_test_host_dp_two_up(struct kunit *test, struct tb_tunnel *a, u32 *a_lanes, + struct tb_tunnel *b, u32 *b_lanes) +{ + tb_test_host_dp_up(test, a, tb_dp_host_rate_for(1), 0, TB_TEST_DOCK_DP_BW, a_lanes); + KUNIT_ASSERT_EQ(test, tb_dp_relink_down(a->host_rate, 2, false), TB_DP_RELINK_NOW); + tb_test_host_dp_up(test, a, tb_dp_host_rate_for(2), 0, TB_TEST_DOCK_DP_BW, a_lanes); + tb_test_host_dp_up(test, b, tb_dp_host_rate_for(2), a->host_rate, + tb_test_host_dp_room(a, *a_lanes), b_lanes); +} + +/* TB_HOST_DP_SHARED_RATE, one display: HBR3 x4, counted so, nothing to re-link. */ +static void tb_test_dp_shared_rate_lone(struct kunit *test) +{ + struct tb_tunnel *a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + u32 lanes; + + KUNIT_ASSERT_NOT_NULL(test, a); + tb_test_host_dp_up(test, a, tb_dp_host_rate_for(1), 0, TB_TEST_DOCK_DP_BW, &lanes); + KUNIT_EXPECT_EQ(test, a->host_rate, 8100); + KUNIT_EXPECT_EQ(test, lanes, 4); + KUNIT_EXPECT_EQ(test, tb_test_host_dp_room(a, lanes), 34650 - 25920); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(a->host_rate, 1, false), TB_DP_RELINK_NONE); + KUNIT_EXPECT_FALSE(test, tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false)); +} + +/* TB_HOST_DP_SHARED_RATE, a second display comes: both HBR2 x4. */ +static void tb_test_dp_shared_rate_two(struct kunit *test) +{ + struct tb_tunnel *a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + struct tb_tunnel *b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + u32 a_lanes, b_lanes; + + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + tb_test_host_dp_two_up(test, a, &a_lanes, b, &b_lanes); + KUNIT_EXPECT_EQ(test, a->host_rate, 5400); + KUNIT_EXPECT_EQ(test, a_lanes, 4); + KUNIT_EXPECT_EQ(test, b->host_rate, 5400); + KUNIT_EXPECT_EQ(test, b_lanes, 4); + /* At HBR2 both: neither is set up again */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(a->host_rate, 2, false), TB_DP_RELINK_NONE); + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(b->host_rate, 2, false), TB_DP_RELINK_NONE); +} + +/* + * TB_HOST_DP_SHARED_RATE, the second display goes: the one left is marked, + * stays capped and counted at HBR2 through the debounce, and is set up again + * at HBR3 x4 once it is over. + */ +static void tb_test_dp_shared_rate_leave(struct kunit *test) +{ + struct tb_tunnel *a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + struct tb_tunnel *b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + const unsigned long due = 1000 + TB_DP_RELINK_DELAY_MS; + u32 a_lanes, b_lanes; + + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + tb_test_host_dp_two_up(test, a, &a_lanes, b, &b_lanes); + + /* B goes at 1000 */ + KUNIT_ASSERT_TRUE(test, tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false)); + a->host_relink = true; + /* Waiting: still capped at HBR2 and counted so */ + KUNIT_EXPECT_EQ(test, a->max_rate, 5400); + KUNIT_EXPECT_EQ(test, tb_test_host_dp_room(a, a_lanes), 34650 - 17280); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due - 1, due), TB_DP_RELINK_WAIT); + + /* Debounce over: set up again, alone, at HBR3 x4 and counted so */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due, due), TB_DP_RELINK_NOW); + tb_test_host_dp_up(test, a, TB_DP_HOST_RATE_ALONE, 0, TB_TEST_DOCK_DP_BW, &a_lanes); + KUNIT_EXPECT_EQ(test, a->host_rate, 8100); + KUNIT_EXPECT_EQ(test, a_lanes, 4); + KUNIT_EXPECT_EQ(test, tb_test_host_dp_room(a, a_lanes), 34650 - 25920); + /* Once: the tunnel set up again is not marked */ + KUNIT_EXPECT_FALSE(test, a->host_relink); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due + 1, due), TB_DP_RELINK_NONE); + KUNIT_EXPECT_FALSE(test, tb_dp_host_drop_relink_up(a)); +} + +/* + * TB_HOST_DP_SHARED_RATE, a second display that comes and goes quickly: no + * re-link while it flaps within the debounce, the debounce starts again at + * each unplug, and a second display soon after a re-link up doubles it. + */ +static void tb_test_dp_shared_rate_flap(struct kunit *test) +{ + struct tb_tunnel *a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + struct tb_tunnel *b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + unsigned int delay = TB_DP_RELINK_DELAY_MS; + u32 a_lanes, b_lanes; + unsigned long due; + + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + tb_test_host_dp_two_up(test, a, &a_lanes, b, &b_lanes); + + /* B goes at 1000 */ + a->host_relink = tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false); + KUNIT_ASSERT_TRUE(test, a->host_relink); + due = 1000 + delay; + /* ... is back at 1500: A held at HBR2, B HBR2 x4 */ + KUNIT_EXPECT_TRUE(test, tb_dp_host_drop_relink_up(a)); + tb_test_host_dp_up(test, b, tb_dp_host_rate_for(2), a->host_rate, + tb_test_host_dp_room(a, a_lanes), &b_lanes); + KUNIT_EXPECT_EQ(test, b->host_rate, 5400); + KUNIT_EXPECT_EQ(test, b_lanes, 4); + /* ... goes again at 2000: a new debounce, the first one's end changes nothing */ + a->host_relink = tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false); + KUNIT_ASSERT_TRUE(test, a->host_relink); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due, 2000 + delay), TB_DP_RELINK_WAIT); + due = 2000 + delay; + /* ... is back at 3000, gone again at 3200 */ + KUNIT_EXPECT_TRUE(test, tb_dp_host_drop_relink_up(a)); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 2, a->host_rate, a->host_max_rate, + false, false, due, due), TB_DP_RELINK_NONE); + a->host_relink = tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false); + due = 3200 + delay; + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due - 1, due), TB_DP_RELINK_WAIT); + + /* Calm until the debounce is over: A set up again at HBR3 x4 */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due, due), TB_DP_RELINK_NOW); + tb_test_host_dp_up(test, a, TB_DP_HOST_RATE_ALONE, 0, TB_TEST_DOCK_DP_BW, &a_lanes); + KUNIT_EXPECT_EQ(test, a->host_rate, 8100); + + /* B is back within a minute: A set up again at HBR2, the next debounce doubled */ + KUNIT_EXPECT_EQ(test, tb_dp_relink_down(a->host_rate, 2, false), TB_DP_RELINK_NOW); + delay = tb_dp_relink_backoff(delay, true); + KUNIT_EXPECT_EQ(test, delay, 5000); + tb_test_host_dp_up(test, a, tb_dp_host_rate_for(2), 0, TB_TEST_DOCK_DP_BW, &a_lanes); + tb_test_host_dp_up(test, b, tb_dp_host_rate_for(2), a->host_rate, + tb_test_host_dp_room(a, a_lanes), &b_lanes); + KUNIT_EXPECT_EQ(test, a->host_rate, 5400); + KUNIT_EXPECT_EQ(test, a_lanes, 4); + KUNIT_EXPECT_EQ(test, b->host_rate, 5400); + KUNIT_EXPECT_EQ(test, b_lanes, 4); + + /* B goes once more at 10000: HBR3 again only after the doubled debounce */ + a->host_relink = tb_dp_relink_up_wanted(1, a->host_rate, a->host_max_rate, false); + due = 10000 + delay; + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, 10000 + TB_DP_RELINK_DELAY_MS, due), + TB_DP_RELINK_WAIT); + KUNIT_EXPECT_EQ(test, tb_dp_relink_up(a->host_relink, 1, a->host_rate, a->host_max_rate, + false, false, due, due), TB_DP_RELINK_NOW); +} + +/* + * A domain with the software connection manager, a host router whose DP IN + * adapters share one link rate (TB_HOST_DP_SHARED_RATE) and a USB4 device + * router with two DP OUT adapters. Adapter registers (TB_CFG_PORT, from the + * adapter capability) are read from @regs: [0] host, [1] device router. + */ +struct tb_test_cm { + struct tb_nhi nhi; + struct tb *tb; + struct tb_switch *host, *dev; + u32 regs[2][20][16]; +}; + +#define TB_TEST_CM_CAP 0x10 +#define TB_TEST_CM_HBR3_X4 (DP_COMMON_CAP_RATE_HBR3 << DP_COMMON_CAP_RATE_SHIFT | \ + DP_COMMON_CAP_4_LANES << DP_COMMON_CAP_LANES_SHIFT) + +static int tb_test_cm_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, + enum tb_cfg_space space, u32 offset, u32 length) +{ + struct tb_test_cm *cm = kunit_get_current_test()->priv; + u32 (*regs)[16] = cm->regs[route ? 1 : 0]; + + if (space != TB_CFG_PORT || port >= ARRAY_SIZE(cm->regs[0]) || + offset < TB_TEST_CM_CAP || offset - TB_TEST_CM_CAP + length > ARRAY_SIZE(regs[0])) + return -EIO; + memcpy(buffer, ®s[port][offset - TB_TEST_CM_CAP], length * sizeof(u32)); + return 0; +} + +static struct tb_test_cm *tb_test_cm_alloc(struct kunit *test) +{ + struct tb_test_cm *cm; + struct tb *tb; + + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); + tb = kunit_kzalloc(test, sizeof(*tb) + tb_test_cm_size(), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tb); + cm->nhi.host_dp_policy = TB_HOST_DP_NOTIFY | TB_HOST_DP_SHARED_RATE; + tb->nhi = &cm->nhi; + device_initialize(&tb->dev); + tb->dev.release = tb_test_dprx_release; + mutex_init(&tb->lock); + tb_test_cm_init(tb); + cm->tb = tb; + + cm->host = alloc_host_usb4(test); + KUNIT_ASSERT_NOT_NULL(test, cm->host); + cm->dev = alloc_dev_usb4(test, cm->host, 0x1, true); + KUNIT_ASSERT_NOT_NULL(test, cm->dev); + cm->host->tb = tb; + cm->dev->tb = tb; + cm->host->config.thunderbolt_version = FIELD_PREP(USB4_VERSION_MAJOR_MASK, 1); + cm->dev->config.thunderbolt_version = FIELD_PREP(USB4_VERSION_MAJOR_MASK, 1); + cm->host->ports[5].cap_adap = TB_TEST_CM_CAP; + cm->host->ports[6].cap_adap = TB_TEST_CM_CAP; + cm->dev->ports[13].cap_adap = TB_TEST_CM_CAP; + cm->dev->ports[14].cap_adap = TB_TEST_CM_CAP; + + test->priv = cm; + kunit_activate_static_stub(test, tb_cfg_read, tb_test_cm_cfg_read); + return cm; +} + +/* + * What the DP IN adapter @in reports once its DPRX is done: HBR3 x4, the + * rate both adapters take, in its caps or (@bw_mode) as its allocated + * bandwidth (26000 Mb/s at a granularity of 250 Mb/s). + */ +static void tb_test_cm_reports(struct tb_test_cm *cm, struct tb_port *in, bool bw_mode) +{ + u32 *regs = cm->regs[0][in->port]; + + regs[DP_LOCAL_CAP] = TB_TEST_CM_HBR3_X4; + regs[DP_COMMON_CAP] = TB_TEST_CM_HBR3_X4 | DP_COMMON_CAP_DPRX_DONE; + regs[ADP_DP_CS_2] = 0; + regs[ADP_DP_CS_8] = bw_mode ? ADP_DP_CS_8_DPME : 0; + regs[DP_STATUS] = bw_mode ? 104 << DP_STATUS_ALLOCATED_BW_SHIFT : 0; +} + +/* + * A host DP tunnel from @in to @out as the capabilities exchange leaves it: + * capped at @rate_limit (Mb/s per lane), up at @rate. Listed by the + * connection manager, its adapters enabled. + */ +static struct tb_tunnel *tb_test_cm_dp_tunnel(struct kunit *test, struct tb_test_cm *cm, + struct tb_port *in, struct tb_port *out, + u32 rate_limit, u32 rate) +{ + struct tb_tunnel *tunnel; + int i; + + tunnel = tb_tunnel_alloc_dp(cm->tb, in, out, 1, 0, 0, tb_test_dp_tunnel_active, NULL); + KUNIT_ASSERT_NOT_NULL(test, tunnel); + tunnel->host_rate_limit = rate_limit; + tunnel->max_rate = min(rate_limit, rate); + tunnel->host_rate = rate; + tunnel->state = TB_TUNNEL_ACTIVE; + for (i = 0; i < tunnel->npaths; i++) + tunnel->paths[i]->activated = true; + list_add_tail(&tunnel->list, tb_test_cm_tunnels(cm->tb)); + cm->regs[0][in->port][ADP_DP_CS_0] = ADP_DP_CS_0_VE | ADP_DP_CS_0_AE; + cm->regs[1][out->port][ADP_DP_CS_0] = ADP_DP_CS_0_VE | ADP_DP_CS_0_AE; + return tunnel; +} + +static void tb_test_cm_dp_tunnel_put(struct tb_tunnel *tunnel) +{ + int i; + + list_del(&tunnel->list); + for (i = 0; i < tunnel->npaths; i++) + tunnel->paths[i]->activated = false; + tunnel->state = TB_TUNNEL_INACTIVE; + tb_tunnel_put(tunnel); +} + +/* + * The DP bandwidth the connection manager counts on the host router's lane + * adapter for a new tunnel from host DP IN 6 to device DP OUT 14. + */ +static int tb_test_cm_counted(struct kunit *test, struct tb_test_cm *cm) +{ + int up, down; + + KUNIT_EXPECT_EQ(test, tb_consumed_dp_bandwidth(cm->tb, &cm->host->ports[6], + &cm->dev->ports[14], &cm->host->ports[1], + &up, &down), 0); + KUNIT_EXPECT_EQ(test, up, 0); + return down; +} + +/* + * TB_HOST_DP_SHARED_RATE, through the connection manager's DP bandwidth + * accounting: a tunnel capped at HBR2 whose DP IN adapter reports HBR3 x4 + * once its DPRX is done is counted at HBR2 x4, from its caps and from its + * allocated bandwidth; capped at HBR3, at HBR3 x4. + */ +static void tb_test_dp_shared_rate_counted(struct kunit *test) +{ + struct tb_test_cm *cm = tb_test_cm_alloc(test); + struct tb_port *in = &cm->host->ports[5]; + struct tb_tunnel *a; + + a = tb_test_cm_dp_tunnel(test, cm, in, &cm->dev->ports[13], 5400, 5400); + KUNIT_ASSERT_TRUE(test, tb_tunnel_port_on_path(a, &cm->host->ports[1])); + + /* Caps: HBR2 x4, not the HBR3 x4 reported */ + tb_test_cm_reports(cm, in, false); + KUNIT_EXPECT_EQ(test, tb_test_cm_counted(test, cm), 17280); + a->max_rate = 8100; + KUNIT_EXPECT_EQ(test, tb_test_cm_counted(test, cm), 25920); + + /* Allocated bandwidth: HBR2 x4, not the 26000 Mb/s reported */ + tb_test_cm_reports(cm, in, true); + a->bw_mode = true; + a->max_rate = 5400; + KUNIT_EXPECT_EQ(test, tb_test_cm_counted(test, cm), 17280); + a->max_rate = 8100; + KUNIT_EXPECT_EQ(test, tb_test_cm_counted(test, cm), 25920); + + tb_test_cm_dp_tunnel_put(a); + put_device(&cm->tb->dev); +} + +/* + * TB_HOST_DP_SHARED_RATE, through the connection manager: A (host DP IN 5 to + * device DP OUT 13) is left alone at HBR2 when B (DP IN 6 to DP OUT 14) goes, + * and C is plugged into DP OUT 14 during the debounce. Pairing drops A's + * re-link up and holds A at HBR2 before C's bandwidth is worked out, and the + * debounce timer is stopped unless deferred pairing (@deferred) needs the + * re-link work. A's DP IN adapter reports HBR3 x4, in its caps or (@bw_mode) + * as its allocated bandwidth; it is counted at HBR2 x4 and C gets HBR2 x4. + */ +static void tb_test_dp_shared_rate_arrival(struct kunit *test, bool bw_mode, bool deferred) +{ + struct tb_test_cm *cm = tb_test_cm_alloc(test); + struct list_head *resources = tb_test_cm_dp_resources(cm->tb); + struct tb_port *in = &cm->host->ports[5]; + struct tb_tunnel *a; + u32 rate, lanes; + int counted; + + /* + * The debounce timer is real: should it expire before the end, its + * work runs on this queue and stops at the missing root switch. + */ + cm->tb->wq = alloc_ordered_workqueue("tb-cm-test", 0); + KUNIT_ASSERT_NOT_NULL(test, cm->tb->wq); + + a = tb_test_cm_dp_tunnel(test, cm, in, &cm->dev->ports[13], 5400, 5400); + a->host_max_rate = 8100; + a->bw_mode = bw_mode; + tb_test_cm_reports(cm, in, bw_mode); + list_add_tail(&cm->host->ports[5].list, resources); + list_add_tail(&cm->host->ports[6].list, resources); + list_add_tail(&cm->dev->ports[13].list, resources); + + mutex_lock(&cm->tb->lock); + /* B went (two were up): A is marked, the debounce armed */ + tb_dp_host_tunnel_gone(cm->tb, 2); + KUNIT_EXPECT_TRUE(test, a->host_relink); + KUNIT_EXPECT_TRUE(test, tb_test_cm_relink_armed(cm->tb)); + KUNIT_EXPECT_EQ(test, kref_read(&cm->tb->dev.kobj.kref), 2U); + tb_test_cm_set_relink_deferred(cm->tb, deferred); + + /* C comes on DP OUT 14: pairing takes HBR2 for it, A's re-link dropped */ + list_add_tail(&cm->dev->ports[14].list, resources); + KUNIT_EXPECT_EQ(test, tb_dp_host_rate_limit(cm->tb), 5400U); + KUNIT_EXPECT_FALSE(test, a->host_relink); + KUNIT_EXPECT_EQ(test, a->host_rate_limit, 5400U); + KUNIT_EXPECT_EQ(test, a->max_rate, 5400U); + KUNIT_EXPECT_EQ(test, tb_test_cm_relink_armed(cm->tb), deferred); + KUNIT_EXPECT_EQ(test, kref_read(&cm->tb->dev.kobj.kref), deferred ? 2U : 1U); + + /* C's bandwidth: A counted at HBR2 x4, C at A's rate gets x4 */ + counted = tb_test_cm_counted(test, cm); + KUNIT_EXPECT_EQ(test, counted, 17280); + KUNIT_EXPECT_EQ(test, tb_dp_host_reduce_bandwidth(TB_TEST_DOCK_DP_BW - counted, 5400, 4, + 5400, 4, a->host_rate, 5400, + &rate, &lanes), 0); + KUNIT_EXPECT_EQ(test, rate, 5400); + KUNIT_EXPECT_EQ(test, lanes, 4); + mutex_unlock(&cm->tb->lock); + + /* Outside the domain lock, which the work takes: drain it, then the queue */ + tb_test_cm_stop(cm->tb); + destroy_workqueue(cm->tb->wq); + KUNIT_EXPECT_EQ(test, kref_read(&cm->tb->dev.kobj.kref), 1U); + INIT_LIST_HEAD(resources); + tb_test_cm_dp_tunnel_put(a); + put_device(&cm->tb->dev); +} + +/* + * TB_HOST_DP_SHARED_RATE, a second display comes during the debounce: it + * gets HBR2 x4 and the debounce timer is stopped, whether the tunnel left + * alone is counted from its caps or from its allocated bandwidth. + */ +static void tb_test_dp_shared_rate_debounce(struct kunit *test) +{ + tb_test_dp_shared_rate_arrival(test, false, false); + tb_test_dp_shared_rate_arrival(test, true, false); +} + +/* + * As tb_test_dp_shared_rate_debounce(), with pairing deferred for a system + * sleep transition: the re-link up is dropped, the re-link work kept armed. + */ +static void tb_test_dp_shared_rate_debounce_deferred(struct kunit *test) +{ + tb_test_dp_shared_rate_arrival(test, false, true); +} + static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_apple_right_dpin_wiring), KUNIT_CASE(tb_test_dp_reduce_bandwidth_shared), + KUNIT_CASE(tb_test_dp_shared_rate_single), + KUNIT_CASE(tb_test_dp_shared_rate_second), + KUNIT_CASE(tb_test_dp_shared_rate_relink_up), + KUNIT_CASE(tb_test_dp_shared_rate_recover), + KUNIT_CASE(tb_test_dp_shared_rate_lone), + KUNIT_CASE(tb_test_dp_shared_rate_two), + KUNIT_CASE(tb_test_dp_shared_rate_leave), + KUNIT_CASE(tb_test_dp_shared_rate_debounce), + KUNIT_CASE(tb_test_dp_shared_rate_debounce_deferred), + KUNIT_CASE(tb_test_dp_shared_rate_flap), + KUNIT_CASE(tb_test_dp_shared_rate_sleep), + KUNIT_CASE(tb_test_dp_shared_rate_retry), + KUNIT_CASE(tb_test_dp_shared_rate_counted), KUNIT_CASE(tb_test_dp_generation), KUNIT_CASE(tb_test_dp_dprx_deferred_first), KUNIT_CASE(tb_test_dp_dprx_cancel), diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index f20b9d4d84a5e..947a297f4d09e 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -919,7 +919,7 @@ static int tb_dp_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes, /* * As tb_dp_reduce_bandwidth(), for a host whose DP IN streams of a PHY share - * one link rate (TB_HOST_DP_HBR2_MAX): the rate is kept and the lanes are + * one link rate (TB_HOST_DP_SHARED_RATE): the rate is kept and the lanes are * reduced (x4, x2, x1). @shared_rate, the rate of a DP tunnel already up on * the same host router, is the only rate allowed (0: none). Without one, the * highest rate both sides take comes first, then the usual combinations. @@ -948,17 +948,38 @@ int tb_dp_reduce_bandwidth_shared(int max_bw, u32 in_rate, u32 in_lanes, u32 out new_rate, new_lanes); } +/* + * A DP tunnel under TB_HOST_DP_SHARED_RATE that has to be reduced, to fit + * @max_bw or to take @shared_rate. Next to another DP tunnel (@shared_rate), + * or alone but held at HBR2 for one on its way (@rate_limit), the rate is + * kept and the lanes go first, see tb_dp_reduce_bandwidth_shared(). Alone at + * up to HBR3, the combination with the most bandwidth is taken, as without + * the policy: HBR2 x4 rather than HBR3 x2. + */ +int tb_dp_host_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes, u32 out_rate, + u32 out_lanes, u32 shared_rate, u32 rate_limit, + u32 *new_rate, u32 *new_lanes) +{ + if (!shared_rate && rate_limit > TB_DP_HOST_RATE_SHARED) + return tb_dp_reduce_bandwidth(max_bw, in_rate, in_lanes, out_rate, + out_lanes, new_rate, new_lanes); + return tb_dp_reduce_bandwidth_shared(max_bw, in_rate, in_lanes, out_rate, + out_lanes, shared_rate, new_rate, new_lanes); +} + /* * The highest DP link rate (Mb/s per lane) a tunnel from @in offers its - * DPTX, 0 for no cap: HBR2 from the DP IN adapters of a host that needs - * one link rate for both DP IN streams of a PHY (TB_HOST_DP_HBR2_MAX). + * DPTX, 0 for no cap. From the DP IN adapters of a host that needs one link + * rate for both DP IN streams of a PHY (TB_HOST_DP_SHARED_RATE), the one the + * connection manager set: HBR3 alone on the host router, HBR2 next to + * another DP tunnel; HBR2 if it set none. */ static u32 tb_dp_host_rate_cap(struct tb_tunnel *tunnel, struct tb_port *in) { if (!tb_port_is_apple_host_dpin(in) || - !tb_host_dp_policy(tunnel->tb->nhi, TB_HOST_DP_HBR2_MAX)) + !tb_host_dp_policy(tunnel->tb->nhi, TB_HOST_DP_SHARED_RATE)) return 0; - return 5400; + return tunnel->host_rate_limit ?: TB_DP_HOST_RATE_SHARED; } /* @val (a DP_LOCAL_CAP) with its rate capped at the tunnel's cap, if any. */ @@ -982,6 +1003,21 @@ int tb_dp_cap_bandwidth(struct tb_tunnel *tunnel, int bw) return tunnel->max_bw ? min(bw, tunnel->max_bw) : bw; } +/* + * The bandwidth (Mb/s) a DP tunnel capped at @max_rate (Mb/s per lane, 0 + * for no cap) is counted at, from the @rate and @lanes read from its DP IN + * adapter. Under TB_HOST_DP_SHARED_RATE the tunnel never runs above its + * cap, but once its DPRX is done what the adapter reports is not bounded by + * it: a tunnel held at HBR2 between adapters that take HBR3 is counted at + * HBR2 all the same, so that a second one next to it gets what HBR2 leaves. + */ +unsigned int tb_dp_host_counted_bandwidth(u32 max_rate, u32 rate, u32 lanes) +{ + if (max_rate && rate > max_rate) + rate = max_rate; + return tb_dp_bandwidth(rate, lanes); +} + static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) { u32 out_dp_cap, out_rate, out_lanes, in_dp_cap, in_rate, in_lanes, bw; @@ -994,6 +1030,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) tunnel->max_rate = tb_dp_host_rate_cap(tunnel, in); tunnel->max_bw = 0; tunnel->host_rate = 0; + tunnel->host_max_rate = 0; /* * Copy DP_LOCAL_CAP register to DP_REMOTE_CAP register for @@ -1021,6 +1058,11 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) if (ret) return ret; + /* What the tunnel could get alone on the host router: HBR3 at most. */ + if (tunnel->max_rate) + tunnel->host_max_rate = min(tb_dp_cap_get_rate(in_dp_cap), + tb_dp_cap_get_rate(out_dp_cap)); + /* * Both sides see the capped rate: the DP OUT through its remote caps, * the DPTX through the DP IN's. @@ -1065,7 +1107,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) max_bw = tunnel->max_up; /* - * Under TB_HOST_DP_HBR2_MAX a second DP tunnel from the host router + * Under TB_HOST_DP_SHARED_RATE a second DP tunnel from the host router * takes the link rate of the one already up, whatever bandwidth there * is: both DP IN streams of the PHY run at one rate. */ @@ -1077,9 +1119,10 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) u32 new_rate, new_lanes, new_bw; if (tunnel->max_rate) - ret = tb_dp_reduce_bandwidth_shared(max_bw, in_rate, in_lanes, - out_rate, out_lanes, shared_rate, - &new_rate, &new_lanes); + ret = tb_dp_host_reduce_bandwidth(max_bw, in_rate, in_lanes, + out_rate, out_lanes, shared_rate, + tunnel->max_rate, &new_rate, + &new_lanes); else ret = tb_dp_reduce_bandwidth(max_bw, in_rate, in_lanes, out_rate, out_lanes, &new_rate, @@ -1684,6 +1727,9 @@ static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel, return ret; if (allocated_bw == max_bw_rounded) allocated_bw = ret; + /* Capped: no more than the capped rate takes, see tb_dp_host_counted_bandwidth(). */ + if (tunnel->max_rate && allocated_bw > ret) + allocated_bw = ret; if (tb_tunnel_direction_downstream(tunnel)) { *consumed_up = 0; @@ -1878,9 +1924,9 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, if (tb_tunnel_direction_downstream(tunnel)) { *consumed_up = 0; - *consumed_down = tb_dp_bandwidth(rate, lanes); + *consumed_down = tb_dp_host_counted_bandwidth(tunnel->max_rate, rate, lanes); } else { - *consumed_up = tb_dp_bandwidth(rate, lanes); + *consumed_up = tb_dp_host_counted_bandwidth(tunnel->max_rate, rate, lanes); *consumed_down = 0; } diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 4cf4e12b77e74..fb5114bbc7563 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -109,8 +109,19 @@ struct tb_tunnel { /* host cap of a DP tunnel's link rate (Mb/s per lane, 0 none), and its bandwidth */ u32 max_rate; int max_bw; - /* under TB_HOST_DP_HBR2_MAX: the link rate the DPTX was offered (Mb/s per lane) */ + /* + * Under TB_HOST_DP_SHARED_RATE (Mb/s per lane): the highest link rate + * the connection manager allows, the one the DPTX was offered, and the + * one both adapters take without the policy. @host_relink: to be set + * up again at a higher rate once the debounce is over (alone now); + * @host_relinked_up: set up so, and set up at HBR2 again if it does + * not come up. + */ + u32 host_rate_limit; u32 host_rate; + u32 host_max_rate; + bool host_relink; + bool host_relinked_up; bool dprx_started; bool dprx_canceled; u64 host_dp_generation; @@ -140,9 +151,74 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, void tb_dp_tunnel_cancel_dprx(struct tb_tunnel *tunnel); bool tb_apple_nhi_needs_bw_grant(struct tb_nhi *nhi); int tb_dp_cap_bandwidth(struct tb_tunnel *tunnel, int bw); +unsigned int tb_dp_host_counted_bandwidth(u32 max_rate, u32 rate, u32 lanes); int tb_dp_reduce_bandwidth_shared(int max_bw, u32 in_rate, u32 in_lanes, u32 out_rate, u32 out_lanes, u32 shared_rate, u32 *new_rate, u32 *new_lanes); +int tb_dp_host_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes, u32 out_rate, + u32 out_lanes, u32 shared_rate, u32 rate_limit, + u32 *new_rate, u32 *new_lanes); u32 tb_dp_host_shared_rate(const struct tb_tunnel *tunnel); + +/* TB_HOST_DP_SHARED_RATE link rates (Mb/s per lane) */ +#define TB_DP_HOST_RATE_ALONE 8100 /* HBR3: one DP tunnel on the host router */ +#define TB_DP_HOST_RATE_SHARED 5400 /* HBR2: more than one */ + +/** + * enum tb_dp_relink - Whether a host DP tunnel is set up again at another rate + * @TB_DP_RELINK_NONE: It is not + * @TB_DP_RELINK_WAIT: Not now, it is decided again later + * @TB_DP_RELINK_NOW: It is, now + */ +enum tb_dp_relink { + TB_DP_RELINK_NONE, + TB_DP_RELINK_WAIT, + TB_DP_RELINK_NOW, +}; + +/* + * TB_HOST_DP_SHARED_RATE re-link work (ms): how long it waits to be decided + * again (also while system sleep is in progress), and the first of its + * TB_DP_RELINK_RETRIES retries, doubled each time, when the domain does not + * runtime resume. + */ +#define TB_DP_RELINK_DELAY_MS 2500 +#define TB_DP_RELINK_POLL_MS 500 +#define TB_DP_RELINK_RETRIES 5 + +/* + * A re-link to a higher rate waits TB_DP_RELINK_DELAY_MS (the debounce), + * doubled up to TB_DP_RELINK_DELAY_MAX_MS while a second DP tunnel takes + * the rate back down within TB_DP_RELINK_CALM_MS of such a re-link. + */ +#define TB_DP_RELINK_DELAY_MAX_MS 40000 +#define TB_DP_RELINK_CALM_MS 60000 + +/** + * struct tb_dp_pm_state - System sleep transitions, for host DP re-links + * @sleeping: From the start of a transition (its PREPARE event, before any + * other notifier) until it is over, its POST callbacks and any + * rollback included (tb_dp_pm_reopen()) + * @reopen: The POST event came: reopen once the transition is over + */ +struct tb_dp_pm_state { + bool sleeping; + bool reopen; +}; + +u32 tb_dp_host_rate_for(unsigned int tunnels); +enum tb_dp_relink tb_dp_relink_down(u32 rate, unsigned int tunnels, bool sleeping); +bool tb_dp_pm_event(struct tb_dp_pm_state *s, unsigned long event); +void tb_dp_pm_reopen(struct tb_dp_pm_state *s); +unsigned int tb_dp_relink_retry_delay(unsigned int tries); +u32 tb_dp_host_rate_after(unsigned int tunnels, bool recovering); +bool tb_dp_relink_up_wanted(unsigned int tunnels, u32 rate, u32 max_rate, bool sleeping); +enum tb_dp_relink tb_dp_relink_up(bool marked, unsigned int tunnels, u32 rate, u32 max_rate, + bool pairable, bool sleeping, unsigned long now, + unsigned long due); +unsigned int tb_dp_relink_backoff(unsigned int delay_ms, bool flapping); +enum tb_dp_relink tb_dp_relink_activation(bool relinked_up, bool active); +bool tb_dp_host_drop_relink_up(struct tb_tunnel *tunnel); + struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, struct tb_port *out, int link_nr, int max_up, int max_down, diff --git a/tools/asahi/tb-dp-repair-tests.py b/tools/asahi/tb-dp-repair-tests.py index 396fdefb5ac04..be775d7618095 100644 --- a/tools/asahi/tb-dp-repair-tests.py +++ b/tools/asahi/tb-dp-repair-tests.py @@ -45,14 +45,14 @@ def function(text, name): #define BIT(n) (1UL<<(n)) #define QUIRK_NO_SYSTEM_SLEEP BIT(5) struct tb {int lock;struct device dev;void*wq;struct tb_switch*root_switch;struct tb_nhi*nhi;}; -struct tb_cm {bool hotplug_active;struct work_struct dp_tunnel_work;bool dp_tunnel_pending;}; +struct tb_cm {bool hotplug_active;struct work_struct dp_tunnel_work;bool dp_tunnel_pending;struct work_struct dp_relink_run;bool dp_host_hold_shared;}; struct domain {struct tb tb;struct tb_cm cm;}; _Static_assert(offsetof(struct domain,cm)==sizeof(struct tb),"mock private storage adjacency"); #define INIT_WORK(w,handler) ((w)->fn=(handler)) struct kref {int count;}; struct tb_path {bool reserved;}; enum {TB_TUNNEL_DP,TB_TUNNEL_USB3}; -struct tb_tunnel {struct kref kref;struct tb*tb;struct tb_port*src_port,*dst_port;int npaths;void(*destroy)(struct tb_tunnel*);struct list_head list;int type;bool dprx_started,dprx_canceled;void(*callback)(struct tb_tunnel*,void*);void*callback_data;struct tb_path*paths[3];}; +struct tb_tunnel {struct kref kref;struct tb*tb;struct tb_port*src_port,*dst_port;int npaths;void(*destroy)(struct tb_tunnel*);struct list_head list;int type;bool dprx_started,dprx_canceled,host_relinked_up;void(*callback)(struct tb_tunnel*,void*);void*callback_data;struct tb_path*paths[3];}; static int tb_tunnel_lock,occupied,alloc_calls,alloc_failures,alloc_successes,freed; static int domain_refs,pm_refs,pm_calls,pm_error,stop_during_pm,requeue_once,lock_depth; static int restore_calls,scan_calls,unplugged_xdomains; @@ -114,6 +114,13 @@ def function(text, name): static void tb_domain_put(struct tb*t){CHECK(domain_refs>1);domain_refs--;} static bool queue_work(void*wq,struct work_struct*w){if(w->pending)return false;w->pending=true;return true;} static void tb_queue_dp_tunnels(struct tb*tb); +/* The TB_HOST_DP_SHARED_RATE re-link work is not queued in these cases. */ +static void tb_dp_relink_run(struct work_struct*w){CHECK(0);} +/* No tunnel here was re-linked at a higher rate, and none waits for it. */ +#define TB_DP_HOST_RATE_SHARED 5400 +enum tb_dp_relink {TB_DP_RELINK_NONE,TB_DP_RELINK_WAIT,TB_DP_RELINK_NOW}; +static enum tb_dp_relink tb_dp_relink_activation(bool relinked_up,bool active){CHECK(!relinked_up);return TB_DP_RELINK_NONE;} +static void tb_dp_relink_replay(struct tb*t){CHECK(lock_depth==1);} static void tb_tunnel_dp(struct tb*t){ CHECK(lock_depth==1 && domain.cm.hotplug_active && t->root_switch); alloc_calls++;if(occupied)alloc_failures++;else alloc_successes++; @@ -298,7 +305,7 @@ def main(): raise ValueError('DPRX reference-release order changed; review extraction') production = '\n'.join(parts.values()) probe = (ROOT / 'drivers/thunderbolt/tb.c').read_text() - probe = probe[probe.index('struct tb *tb_probe(struct tb_nhi *nhi)'):] + probe = probe[probe.index('static void tb_cm_init(struct tb_cm *tcm)'):] initializers = re.findall(r'INIT_WORK\(&tcm->\w+,\s*\w+\);', probe) production += '\nstatic void init_fixture(struct tb_cm*tcm){\n' + '\n'.join(initializers) + '\n}\n' results = {}