net: eth-uclass: avoid running start() twice without stop()
authorMatthias Schiffer <matthias.schiffer@ew.tq-group.com>
Wed, 4 Nov 2020 13:45:14 +0000 (14:45 +0100)
committerTom Rini <trini@konsulko.com>
Tue, 19 Jan 2021 14:06:15 +0000 (09:06 -0500)
Running the start() handler twice without a stop() inbetween completely
breaks communication for some ethernet drivers like fec_mxc.

eth_halt() is called before each eth_init(). Due to the switch to
eth_is_active() in commit 68acb51f442f ("net: Only call halt on a driver
that has been init'ed"), this is not sufficient anymore when netconsole
is active: eth_init_state_only()/eth_halt_state_only() manipulate the
state check that is performed by eth_is_active() without actually
calling into the driver.

The issue can be triggered by starting a network operation (e.g. ping or
tftp) while netconsole is active.

Add an additional "running" flag that reflects the actual state of the
driver and use it to ensure that eth_halt() actually stops the device as
it is supposed to.

Fixes: 68acb51f442f ("net: Only call halt on a driver that has been init'ed")
Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
net/eth-uclass.c

index 0156324..7c2454f 100644 (file)
@@ -26,6 +26,7 @@ DECLARE_GLOBAL_DATA_PTR;
  */
 struct eth_device_priv {
        enum eth_state_t state;
+       bool running;
 };
 
 /**
@@ -290,6 +291,7 @@ int eth_init(void)
                                                dev_get_uclass_priv(current);
 
                                        priv->state = ETH_STATE_ACTIVE;
+                                       priv->running = true;
                                        return 0;
                                }
                        } else {
@@ -319,13 +321,16 @@ void eth_halt(void)
        struct eth_device_priv *priv;
 
        current = eth_get_dev();
-       if (!current || !eth_is_active(current))
+       if (!current)
                return;
 
-       eth_get_ops(current)->stop(current);
        priv = dev_get_uclass_priv(current);
-       if (priv)
-               priv->state = ETH_STATE_PASSIVE;
+       if (!priv || !priv->running)
+               return;
+
+       eth_get_ops(current)->stop(current);
+       priv->state = ETH_STATE_PASSIVE;
+       priv->running = false;
 }
 
 int eth_is_active(struct udevice *dev)
@@ -534,6 +539,7 @@ static int eth_post_probe(struct udevice *dev)
 #endif
 
        priv->state = ETH_STATE_INIT;
+       priv->running = false;
 
        /* Check if the device has a valid MAC address in device tree */
        if (!eth_dev_get_mac_address(dev, pdata->enetaddr) ||