[net,v4,15/15] net: stmmac: retain DMA resources across MTU changes
Commit Message
Prepare the replacement configuration before quiescing the old datapath.
Retain its rings and IRQ registrations until setup succeeds so rollback
needs no new allocations or IRQ requests. Fill RX buffer holes before
reset without altering active descriptors; rebuild the retained rings
only after a successful reset.
Mask the device DMA interrupt sources, gate shared-IRQ handlers and
drain all registered handlers before the final transmitter and timer
cancellation. Restore saved interrupt masks only after the selected
rings and NAPI are ready. This leaves interrupt-controller lines
available to unrelated devices.
Program receive limits using the prospective MTU and restore the old MTU
before rollback. Reapply PHC and TC state before starting DMA,
preserving the PHC registration and packet timestamp filters. Continuous
PHC time across the reset is not preserved.
If rollback fails, leave the administratively-up interface detached in a
distinct HALTED state: rings retained, NAPI disabled and IRQ
registrations released. Close or a later down/up can finish cleanup and
recovery without freeing IRQs twice. Retain potentially active DMA
memory until hardware shutdown is confirmed.
Fixes: 3470079687448 ("net: ethernet: stmicro: stmmac: permit MTU change with interface up")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 216 +++++++++++++++++-----
2 files changed, 173 insertions(+), 45 deletions(-)
Comments
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] net: stmmac: double disable of NAPI causing deadlock in stmmac_reconfigure_mtu()
--
commit ed7896fe947ec45eb3dddd37b9ca9ed5f20ea4e9
Author: James Hilliard <james.hilliard1@gmail.com>
net: stmmac: retain DMA resources across MTU changes
This commit modifies the MTU change procedure to prepare the replacement
configuration before quiescing the old datapath. It retains the rings and IRQ
registrations until setup succeeds so rollback needs no new allocations, and
leaves the interface detached in a distinct HALTED state if rollback fails.
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
[ ... ]
> @@ -6601,6 +6651,109 @@ static void stmmac_set_rx_mode(struct net_device *dev)
> stmmac_set_filter(priv, priv->hw, dev);
> }
>
> +static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)
> +{
> + struct stmmac_priv *priv = netdev_priv(dev);
> + struct stmmac_dma_conf *old_conf = priv->dma_conf;
> + struct stmmac_dma_conf *new_conf;
> + int old_mtu = dev->mtu;
> + int ret, restore_ret;
> + u32 irq_mask[STMMAC_CH_MAX];
> + u32 chan;
> +
> + new_conf = stmmac_setup_dma_desc(priv, mtu);
> + if (IS_ERR(new_conf))
> + return PTR_ERR(new_conf);
> +
> + mutex_lock(&priv->ptp_mutex);
> + stmmac_block_ptp(priv, true);
> + netif_device_detach(dev);
> + phylink_stop(priv->phylink);
> + stmmac_quiesce(priv);
[Severity: High]
Does this unconditional call to stmmac_quiesce() lead to a double-disable of
NAPI if the interface is already in the HALTED state?
If stmmac_reconfigure_mtu() fails to rollback after a hardware setup error,
the interface state is set to STMMAC_DATAPATH_HALTED with NAPI left disabled,
but the device remains administratively up (netif_running(dev) remains true).
If an MTU change is attempted again, stmmac_change_mtu() will call
stmmac_reconfigure_mtu() a second time. This unconditional stmmac_quiesce()
call will invoke stmmac_disable_all_queues(), resulting in a second
napi_disable() on the already disabled NAPI instances. This can cause the
kernel to deadlock in napi_disable_locked() while waiting for
NAPIF_STATE_SCHED to clear.
> + if (stmmac_fpe_supported(priv))
> + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
[ ... ]
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 4 · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] stmmac_reconfigure_mtu()
(drivers/net/ethernet/stmicro/stmmac/stmmac_main.c) calls…
- [Medium] stmmac_reconfigure_mtu() takes priv->ptp_mutex
(stmmac_main.c:6668) before it calls phylink_stop().
- [Medium] stmmac_reconfigure_mtu() masks only the per-channel DMA
interrupt enables (stmmac_set_dma_irq_state -> set_irq_mask).
- [Medium] Before the first stmmac_hw_setup(), stmmac_reconfigure_mtu()
calls phylink_prepare_resume().
--
net: stmmac: retain DMA resources across MTU changes
This patch replaces the release/reopen sequence for live jumbo MTU
changes with a new function, stmmac_reconfigure_mtu(). The new function
keeps the old rings and IRQ registrations so a failed setup can be rolled
back. If the rollback also fails, the interface is left detached in a new
STMMAC_DATAPATH_HALTED state that close or a later down/up can clean up.
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 08049c05ffc48..8964f52c26bae 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
[ ... ]
> @@ -6601,6 +6651,109 @@ static void stmmac_set_rx_mode(struct net_device *dev)
> stmmac_set_filter(priv, priv->hw, dev);
> }
>
> +static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)
> +{
[ ... ]
> + mutex_lock(&priv->ptp_mutex);
> + stmmac_block_ptp(priv, true);
> + netif_device_detach(dev);
> + phylink_stop(priv->phylink);
[Severity: Medium]
Can holding ptp_mutex across phylink_stop() create a lock ordering cycle?
phylink_stop()->phylink_run_resolve_and_disable() does:
queue_work(system_power_efficient_wq, &pl->resolve);
flush_work(&pl->resolve);
so lockdep records a ptp_mutex -> resolve work dependency.
On link down, the resolve worker runs
phylink_resolve()->phylink_link_down()->phylink_deactivate_lpi()->
stmmac_mac_disable_tx_lpi(), and that takes priv->lock. With MAC WoL,
phylink_stop() also calls phylink_link_down() directly under state_mutex.
stmmac_resume() takes the two locks in the opposite order:
mutex_lock(&priv->lock);
...
mutex_lock(&priv->ptp_mutex);
Together these form ptp_mutex -> resolve work -> priv->lock -> ptp_mutex.
Both the MTU path and the resume path hold RTNL today, so this may not
deadlock in practice. The resolve worker does not hold RTNL, though.
Once an MTU change, an LPI link-down and a resume have all run, won't
lockdep report a circular dependency? Before this patch, nothing called
phylink_stop() with ptp_mutex held.
Could ptp_mutex be taken after phylink_stop() and stmmac_quiesce()
instead?
> + stmmac_quiesce(priv);
> + if (stmmac_fpe_supported(priv))
> + ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
> +
> + /* Drain handlers before the final TX stop and configuration swap,
> + * and keep the registrations for rollback.
> + */
> + stmmac_set_dma_irq_state(priv, false, irq_mask);
> + stmmac_synchronize_irq(priv);
[Severity: Medium]
Only the per-channel DMA interrupt enables are masked here. What happens
to the MAC core interrupt sources? On dwmac4, dwmac4_core_init() writes
GMAC_INT_DEFAULT_ENABLE (PMT, LPI and TSIE) to GMAC_INT_EN, and none of
those are masked.
While ptp_blocked is set, stmmac_common_interrupt() skips the timestamp
acknowledgement:
if (!READ_ONCE(priv->ptp_blocked))
stmmac_timestamp_interrupt(priv, priv);
The only place that clears the TS interrupt is timestamp_interrupt(),
which reads GMAC_TIMESTAMP_STATUS.
Suppose an extts snapshot or a PPS target time event arrives between
stmmac_block_ptp(priv, true) and the unblock at the restart label. Can a
level-triggered line then keep firing, with stmmac_interrupt() returning
IRQ_HANDLED every time? That window includes the sleeping
phylink_stop()/flush_work() and synchronize_net(). It also covers the
time after stmmac_ptp_restore() re-arms extts/PPS inside
stmmac_hw_setup().
On a single-CPU system, could this stop the thread that performs the
reset from ever running?
The failed rollback branch further down has a related problem:
stmmac_set_dma_irq_state(priv, false, irq_mask);
stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
...
priv->datapath = STMMAC_DATAPATH_HALTED;
The failed stmmac_hw_setup() has already run stmmac_core_init(), which
re-enables the MAC interrupts, before its later fallible steps
(stmmac_rxp_config(), stmmac_tc_restore_filters(),
stmmac_restore_timestamping(), stmmac_tc_restore_est()).
stmmac_mac_set(false) only clears RE/TE.
stmmac_request_irq_single() registers dev->irq with IRQF_SHARED. If the
MAC raises LPI, TS, PMT or safety status after the handler is freed,
could the shared line storm until the IRQ core disables it? That would
break the unrelated devices that the commit message says this design
keeps working. The old failed-reopen path behaved similarly for this
second case, but the ptp_blocked window above is new.
> + stmmac_stop_tx_queues(priv);
> +
> + ret = stmmac_prepare_rx_buffers(priv);
> + if (ret)
> + goto restart;
> +
> + stmmac_stop_all_dma(priv);
> + phylink_prepare_resume(priv->phylink);
> +
> + /* MAC receive limits must be programmed for the prospective MTU. */
> + WRITE_ONCE(dev->mtu, mtu);
> + priv->dma_conf = new_conf;
> + stmmac_reset_queues_param(priv);
> + ret = stmmac_hw_setup(dev, false, true);
[Severity: Medium]
Does passing keep_ptp=true here still work if the PTP reference clock
was never enabled at open?
stmmac_setup_ptp() only warns when the clock fails to enable:
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
priv->ptp_clock_enabled = !ret;
...
if (ret < 0) {
netdev_warn(...);
return;
}
However, stmmac_restore_timestamping() checks only dma_cap and
clk_ptp_rate before it programs the counter. It does not look at
priv->ptp_clock_enabled:
if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) ||
!priv->plat->clk_ptp_rate)
return 0;
ret = stmmac_init_tstamp_counter(priv, priv->systime_flags);
Further down,
stmmac_hw_setup()->stmmac_restore_timestamping()->stmmac_ptp_restore()->
config_addend() polls TSADDREG for up to 100ms:
return readl_poll_timeout_atomic(ioaddr + PTP_TCR, value,
!(value & PTP_TCR_TSADDREG),
10, 100000);
On hardware where this handshake cannot complete while the reference
clock is gated, the rollback stmmac_hw_setup(dev, true, true) would fail
the same way. Wouldn't that leave the interface in
STMMAC_DATAPATH_HALTED?
Before this patch, the MTU path went through __stmmac_open(). That called
stmmac_hw_setup() with keep_ptp=false and then stmmac_setup_ptp(), which
tolerates this failure. Should stmmac_restore_timestamping() also check
priv->ptp_clock_enabled?
> + if (ret) {
> + stmmac_stop_all_dma(priv);
> + stmmac_mac_set(priv, priv->ioaddr, false);
> + priv->dma_conf = old_conf;
> + WRITE_ONCE(dev->mtu, old_mtu);
> +
> + /* Reuse the retained rings. Reinitialize them only after reset
> + * has completed, not merely after clearing the DMA enable bits.
> + */
> + restore_ret = stmmac_hw_setup(dev, true, true);
> + if (restore_ret) {
> + stmmac_stop_all_dma(priv);
> + stmmac_mac_set(priv, priv->ioaddr, false);
> + /* Setup may have restored DMA interrupt enables. */
> + stmmac_set_dma_irq_state(priv, false, irq_mask);
> + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
> + stmmac_stop_tx_queues(priv);
> + stmmac_stop_all_dma(priv);
> + memset(irq_mask, 0, sizeof(irq_mask));
> + stmmac_set_dma_irq_state(priv, true, irq_mask);
> + priv->datapath = STMMAC_DATAPATH_HALTED;
> + netdev_err(dev, "MTU rollback failed: %pe; interface remains detached\n",
> + ERR_PTR(restore_ret));
> + goto free_new;
[Severity: Medium]
Does anything undo the earlier phylink_prepare_resume() on this path?
On mac_requires_rxc platforms, phylink_prepare_resume() calls
phy_resume() on the PHY that phylink_stop() has just suspended. The
HALTED branch never calls phylink_stop() or phy_suspend(). The PHY stays
powered and can present link to the partner while the interface is
detached. That lasts until close, when __stmmac_release() calls
phylink_stop().
The init_error path in __stmmac_open() does undo it:
init_error:
phylink_stop(priv->phylink);
stmmac_drain_dma(priv);
phylink_stop(priv->phylink);
Should the HALTED branch also call phylink_stop() before it returns?
> + }
[ ... ]
@@ -279,6 +279,8 @@ enum stmmac_datapath_state {
STMMAC_DATAPATH_RUNNING,
/* Resources retained, NAPI disabled, DMA stop requested. */
STMMAC_DATAPATH_SUSPENDED,
+ /* Failed MTU rollback: rings retained, but no IRQs or running NAPI. */
+ STMMAC_DATAPATH_HALTED,
};
struct stmmac_priv {
@@ -977,7 +977,7 @@ static void stmmac_release_ptp(struct stmmac_priv *priv)
}
/* ptp_mutex excludes configuration and crosstimestamp operations. The
- * spinlock also excludes atomic clock reads while changing this gate.
+ * spinlock also excludes atomic gettime callers while changing this gate.
*/
static void stmmac_block_ptp(struct stmmac_priv *priv, bool block)
{
@@ -2205,6 +2205,30 @@ static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)
dma_free_tx_skbufs(priv, priv->dma_conf, queue);
}
+/* NAPI is stopped, but DMA may still be using the old rings. Fill holes in
+ * the software buffer array without changing any descriptors. If allocation
+ * fails, the old rings can continue unchanged. Otherwise rollback after a
+ * reset will not need to allocate buffers.
+ */
+static int stmmac_prepare_rx_buffers(struct stmmac_priv *priv)
+{
+ struct stmmac_dma_conf *dma_conf = priv->dma_conf;
+ u32 queue, i;
+ int ret;
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ ret = stmmac_alloc_rx_buffer(priv, rx_q, &rx_q->buf_pool[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/* Only after a successful DMA reset, and with all RX buffers prepared. */
static void stmmac_reinit_dma_desc(struct stmmac_priv *priv)
{
@@ -4311,14 +4335,38 @@ static void stmmac_synchronize_irq(struct stmmac_priv *priv)
synchronize_irq(msi->tx_irq[i]);
}
+/* Keep the IRQ registrations, but prevent DMA handlers from using the rings.
+ * The caller drains handlers after quiescing every channel and restores their
+ * masks only once the active DMA configuration is ready again.
+ */
+static void stmmac_set_dma_irq_state(struct stmmac_priv *priv, bool enable,
+ u32 *irq_mask)
+{
+ u32 channels = max(priv->plat->rx_queues_to_use,
+ priv->plat->tx_queues_to_use);
+ u32 chan;
+
+ for (chan = 0; chan < channels; chan++) {
+ struct stmmac_channel *ch = &priv->channel[chan];
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->irq_quiesced = !enable;
+ if (enable)
+ stmmac_set_dma_irq_mask(priv, priv->ioaddr, chan,
+ irq_mask[chan]);
+ else
+ irq_mask[chan] = stmmac_set_dma_irq_mask(priv, priv->ioaddr,
+ chan, 0);
+ spin_unlock_irqrestore(&ch->lock, flags);
+ }
+}
+
/**
- * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue
- * @priv: driver private structure
- * @mtu: MTU to setup the dma queue and buf with
- * Description: Allocate and generate a dma_conf based on the provided MTU.
- * Allocate the Tx/Rx DMA queue and init them.
- * Return value:
- * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.
+ * stmmac_setup_dma_desc - allocate and initialize a DMA configuration
+ * @priv: driver private structure
+ * @mtu: MTU to size the receive buffers for
+ * Return: the allocated configuration, or an ERR_PTR on failure
*/
static struct stmmac_dma_conf *
stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
@@ -4544,7 +4592,7 @@ static int __stmmac_open(struct net_device *dev,
priv->dma_conf = dma_conf;
/* The PHY is suspended when the interface is reopened without
- * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs
+ * disconnecting the PHY, e.g. on an ethtool change. IEEE 802.3 allows PHYs
* to stop their receive clock while powered down, but the DMA
* software reset in stmmac_hw_setup() requires a running receive
* clock, and phylink_start() below resumes the PHY only after the
@@ -4699,20 +4747,22 @@ static void stmmac_quiesce(struct stmmac_priv *priv)
static void __stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ enum stmmac_datapath_state state = priv->datapath;
- /* A failed MTU reopen has already released the data path. */
+ /* There may be no resources left after detached XDP reconfiguration. */
if (priv->datapath == STMMAC_DATAPATH_DOWN)
return;
phylink_stop(priv->phylink);
- /* Suspend retains the resources, but has already stopped activity. */
+ /* SUSPENDED and HALTED retain rings with NAPI already disabled. */
if (priv->datapath == STMMAC_DATAPATH_RUNNING)
stmmac_quiesce(priv);
priv->datapath = STMMAC_DATAPATH_DOWN;
/* Free the IRQ lines */
- stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ if (state != STMMAC_DATAPATH_HALTED)
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
/* Drain any final IRQ-triggered network activity before DMA shutdown. */
stmmac_stop_tx_queues(priv);
@@ -6601,6 +6651,109 @@ static void stmmac_set_rx_mode(struct net_device *dev)
stmmac_set_filter(priv, priv->hw, dev);
}
+static int stmmac_reconfigure_mtu(struct net_device *dev, int mtu)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_dma_conf *old_conf = priv->dma_conf;
+ struct stmmac_dma_conf *new_conf;
+ int old_mtu = dev->mtu;
+ int ret, restore_ret;
+ u32 irq_mask[STMMAC_CH_MAX];
+ u32 chan;
+
+ new_conf = stmmac_setup_dma_desc(priv, mtu);
+ if (IS_ERR(new_conf))
+ return PTR_ERR(new_conf);
+
+ mutex_lock(&priv->ptp_mutex);
+ stmmac_block_ptp(priv, true);
+ netif_device_detach(dev);
+ phylink_stop(priv->phylink);
+ stmmac_quiesce(priv);
+ if (stmmac_fpe_supported(priv))
+ ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+
+ /* Drain handlers before the final TX stop and configuration swap,
+ * and keep the registrations for rollback.
+ */
+ stmmac_set_dma_irq_state(priv, false, irq_mask);
+ stmmac_synchronize_irq(priv);
+ stmmac_stop_tx_queues(priv);
+
+ ret = stmmac_prepare_rx_buffers(priv);
+ if (ret)
+ goto restart;
+
+ stmmac_stop_all_dma(priv);
+ phylink_prepare_resume(priv->phylink);
+
+ /* MAC receive limits must be programmed for the prospective MTU. */
+ WRITE_ONCE(dev->mtu, mtu);
+ priv->dma_conf = new_conf;
+ stmmac_reset_queues_param(priv);
+ ret = stmmac_hw_setup(dev, false, true);
+ if (ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ priv->dma_conf = old_conf;
+ WRITE_ONCE(dev->mtu, old_mtu);
+
+ /* Reuse the retained rings. Reinitialize them only after reset
+ * has completed, not merely after clearing the DMA enable bits.
+ */
+ restore_ret = stmmac_hw_setup(dev, true, true);
+ if (restore_ret) {
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+ /* Setup may have restored DMA interrupt enables. */
+ stmmac_set_dma_irq_state(priv, false, irq_mask);
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ stmmac_stop_tx_queues(priv);
+ stmmac_stop_all_dma(priv);
+ memset(irq_mask, 0, sizeof(irq_mask));
+ stmmac_set_dma_irq_state(priv, true, irq_mask);
+ priv->datapath = STMMAC_DATAPATH_HALTED;
+ netdev_err(dev, "MTU rollback failed: %pe; interface remains detached\n",
+ ERR_PTR(restore_ret));
+ goto free_new;
+ }
+ } else {
+ /* Hardware setup completed its reset before using the new rings.
+ * The old DMA allocations can now be released safely.
+ */
+ stmmac_put_dma_conf(priv, old_conf);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ hrtimer_setup(&new_conf->tx_queue[chan].txtimer,
+ stmmac_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ }
+
+ stmmac_set_rx_mode(dev);
+ stmmac_vlan_restore(priv);
+ stmmac_start_all_dma(priv);
+
+restart:
+ stmmac_block_ptp(priv, false);
+ mutex_unlock(&priv->ptp_mutex);
+ stmmac_enable_all_queues(priv);
+ stmmac_set_dma_irq_state(priv, true, irq_mask);
+ stmmac_enable_all_dma_irq(priv);
+ phylink_start(priv->phylink);
+ netif_device_attach(dev);
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ stmmac_tx_timer_arm(priv, chan);
+ if (!ret)
+ return 0;
+ goto free_conf;
+
+free_new:
+ /* Failed rollback leaves the registered PHC inaccessible as well. */
+ mutex_unlock(&priv->ptp_mutex);
+free_conf:
+ stmmac_put_dma_conf(priv, new_conf);
+ return ret;
+}
+
/**
* stmmac_change_mtu - entry point to change MTU size for the device.
* @dev : device pointer.
@@ -6615,9 +6768,7 @@ static void stmmac_set_rx_mode(struct net_device *dev)
static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
- struct stmmac_dma_conf *old_conf = priv->dma_conf;
int txfifosz = priv->plat->tx_fifo_size;
- struct stmmac_dma_conf *dma_conf;
const int mtu = new_mtu;
int ret;
@@ -6643,35 +6794,9 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
*/
if (netif_running(dev) &&
(dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) {
- netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
- /* Try to allocate the new DMA conf with the new mtu */
- dma_conf = stmmac_setup_dma_desc(priv, mtu);
- if (IS_ERR(dma_conf)) {
- netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
- mtu);
- return PTR_ERR(dma_conf);
- }
-
- netif_device_detach(dev);
- __stmmac_release(dev);
-
- ret = __stmmac_open(dev, dma_conf);
- if (ret) {
- priv->dma_conf = old_conf;
- stmmac_put_dma_conf(priv, dma_conf);
- /*
- * Keep the administrative state and PHY/PM ownership until
- * ndo_stop(), but prevent use of the released data path.
- */
- netif_device_detach(dev);
- netdev_err(priv->dev, "failed reopening the interface after MTU change\n");
+ ret = stmmac_reconfigure_mtu(dev, mtu);
+ if (ret)
return ret;
- }
-
- stmmac_put_dma_conf(priv, old_conf);
-
- stmmac_set_rx_mode(dev);
- netif_device_attach(dev);
}
WRITE_ONCE(dev->mtu, mtu);
@@ -7514,11 +7639,12 @@ static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
return -EOPNOTSUPP;
/*
- * Pool removal must succeed even after a failed resume. Release the
- * suspended rings before their pool or XDP buffer layout can change.
+ * Pool removal must succeed after failed resume or MTU rollback. Release
+ * retained rings before their pool or XDP buffer layout can change.
* Leave the interface detached until it is closed and reopened.
*/
- if (priv->datapath == STMMAC_DATAPATH_SUSPENDED)
+ if (priv->datapath == STMMAC_DATAPATH_SUSPENDED ||
+ priv->datapath == STMMAC_DATAPATH_HALTED)
__stmmac_release(dev);
switch (bpf->command) {