diff options
author | Matthias Schiffer <matthias.schiffer@ew.tq-group.com> | 2024-04-26 10:02:24 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2024-05-15 10:46:47 -0600 |
commit | ef9d4da6c92b07f0668f059d0cdc51eb34ed947e (patch) | |
tree | f1c9b5e3dcb2d5fc3c5905a9cd260f5040af8165 | |
parent | 85e2c6900ea3282b766ffc37c71f035b0f47395f (diff) | |
download | u-boot-ef9d4da6c92b07f0668f059d0cdc51eb34ed947e.tar.gz u-boot-ef9d4da6c92b07f0668f059d0cdc51eb34ed947e.tar.bz2 u-boot-ef9d4da6c92b07f0668f059d0cdc51eb34ed947e.zip |
net: eth-uclass: guard against reentrant eth_init()/eth_halt() calls
With netconsole, any log message can result in an eth_init(), possibly
causing an reentrant call into eth_init() if a driver's ops print
anything:
eth_init() -> driver.start() -> printf() -> netconsole -> eth_init()
eth_halt() -> driver.stop() -> printf() -> netconsole -> eth_init()
Rather than expecting every single Ethernet driver to handle this case,
prevent the reentrant calls in eth_init() and eth_halt().
The issue was noticed on an AM62x board, where a bootm after
simultaneous netconsole and TFTP would result in a crash.
Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Link: https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1350550/sk-am62a-lp-am65_cpsw_nuss_port-error-in-u-boot-while-using-netconsole-functionality/
-rw-r--r-- | net/eth-uclass.c | 40 |
1 files changed, 32 insertions, 8 deletions
diff --git a/net/eth-uclass.c b/net/eth-uclass.c index 4e3933fd05..e34d7af022 100644 --- a/net/eth-uclass.c +++ b/net/eth-uclass.c @@ -47,6 +47,8 @@ struct eth_uclass_priv { /* eth_errno - This stores the most recent failure code from DM functions */ static int eth_errno; +/* Are we currently in eth_init() or eth_halt()? */ +static bool in_init_halt; /* board-specific Ethernet Interface initializations. */ __weak int board_interface_eth_init(struct udevice *dev, @@ -284,11 +286,19 @@ U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr); int eth_init(void) { - char *ethact = env_get("ethact"); - char *ethrotate = env_get("ethrotate"); struct udevice *current = NULL; struct udevice *old_current; int ret = -ENODEV; + char *ethrotate; + char *ethact; + + if (in_init_halt) + return -EBUSY; + + in_init_halt = true; + + ethact = env_get("ethact"); + ethrotate = env_get("ethrotate"); /* * When 'ethrotate' variable is set to 'no' and 'ethact' variable @@ -297,8 +307,10 @@ int eth_init(void) if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) { if (ethact) { current = eth_get_dev_by_name(ethact); - if (!current) - return -EINVAL; + if (!current) { + ret = -EINVAL; + goto end; + } } } @@ -306,7 +318,8 @@ int eth_init(void) current = eth_get_dev(); if (!current) { log_err("No ethernet found.\n"); - return -ENODEV; + ret = -ENODEV; + goto end; } } @@ -323,7 +336,8 @@ int eth_init(void) priv->state = ETH_STATE_ACTIVE; priv->running = true; - return 0; + ret = 0; + goto end; } } else { ret = eth_errno; @@ -343,6 +357,8 @@ int eth_init(void) current = eth_get_dev(); } while (old_current != current); +end: + in_init_halt = false; return ret; } @@ -351,17 +367,25 @@ void eth_halt(void) struct udevice *current; struct eth_device_priv *priv; + if (in_init_halt) + return; + + in_init_halt = true; + current = eth_get_dev(); if (!current) - return; + goto end; priv = dev_get_uclass_priv(current); if (!priv || !priv->running) - return; + goto end; eth_get_ops(current)->stop(current); priv->state = ETH_STATE_PASSIVE; priv->running = false; + +end: + in_init_halt = false; } int eth_is_active(struct udevice *dev) |