aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2021-11-24 18:40:09 -0800
committerJakub Kicinski <kuba@kernel.org>2021-11-24 18:40:09 -0800
commit06e5ba71750861acf65fb1b0cfb8f974159100af (patch)
treefc745cb9ac180c3db85443ab3baad920eaf7bd66
parentddb826c2c92d461f290a7bab89e7c28696191875 (diff)
parentdbae3388ea9ca33bd1d5eabc3b0ef17e69c74677 (diff)
downloadlinux-06e5ba71750861acf65fb1b0cfb8f974159100af.tar.gz
Merge branch 'phylink-resolve-fixes'
Marek BehĂșn says: ==================== phylink resolve fixes With information from me and my nagging, Russell has produced two fixes for phylink, which add code that triggers another phylink_resolve() from phylink_resolve(), if certain conditions are met: interface is being changed or link is down and previous link was up These are needed because sometimes the PCS callbacks may provide stale values if link / speed / ... ==================== Link: https://lore.kernel.org/r/20211123154403.32051-1-kabel@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/phy/phylink.c26
1 files changed, 25 insertions, 1 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 3ad7397b81198..5904546acae61 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -710,6 +710,7 @@ static void phylink_resolve(struct work_struct *w)
struct phylink_link_state link_state;
struct net_device *ndev = pl->netdev;
bool mac_config = false;
+ bool retrigger = false;
bool cur_link_state;
mutex_lock(&pl->state_mutex);
@@ -723,6 +724,7 @@ static void phylink_resolve(struct work_struct *w)
link_state.link = false;
} else if (pl->mac_link_dropped) {
link_state.link = false;
+ retrigger = true;
} else {
switch (pl->cur_link_an_mode) {
case MLO_AN_PHY:
@@ -739,6 +741,19 @@ static void phylink_resolve(struct work_struct *w)
case MLO_AN_INBAND:
phylink_mac_pcs_get_state(pl, &link_state);
+ /* The PCS may have a latching link-fail indicator.
+ * If the link was up, bring the link down and
+ * re-trigger the resolve. Otherwise, re-read the
+ * PCS state to get the current status of the link.
+ */
+ if (!link_state.link) {
+ if (cur_link_state)
+ retrigger = true;
+ else
+ phylink_mac_pcs_get_state(pl,
+ &link_state);
+ }
+
/* If we have a phy, the "up" state is the union of
* both the PHY and the MAC
*/
@@ -747,6 +762,15 @@ static void phylink_resolve(struct work_struct *w)
/* Only update if the PHY link is up */
if (pl->phydev && pl->phy_state.link) {
+ /* If the interface has changed, force a
+ * link down event if the link isn't already
+ * down, and re-resolve.
+ */
+ if (link_state.interface !=
+ pl->phy_state.interface) {
+ retrigger = true;
+ link_state.link = false;
+ }
link_state.interface = pl->phy_state.interface;
/* If we have a PHY, we need to update with
@@ -789,7 +813,7 @@ static void phylink_resolve(struct work_struct *w)
else
phylink_link_up(pl, link_state);
}
- if (!link_state.link && pl->mac_link_dropped) {
+ if (!link_state.link && retrigger) {
pl->mac_link_dropped = false;
queue_work(system_power_efficient_wq, &pl->resolve);
}