[PATCH net-next v7 11/14] net: lan966x: add PCIe FDMA MTU change support

Daniel Machon daniel.machon at microchip.com
Wed Sep 23 02:46:22 PDT 2026


> Thank you for your contribution! Sashiko AI review found 4 potential
> issue(s) to consider.
> 
> Critical: 0 · High: 2 (2) · Medium: 1 (1) · Low: 1
> 
> 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).
> 
> - [High] MTU change during or after PCIe FDMA teardown runs the full
>   reload on freed/quiesced state: unbounded napi_disable() while holding…
> - [High] The new resize readiness check publishes lan966x->rx.lan966x
>   before the PCI FDMA rings exist, so an MTU change can race with ring…
> - [Medium] The new PCIe resize path retires DMA rings and tears down
>   their ATU outbound window without ever confirming the hardware is…
> - [Low] FDMA_PCI_DB_SIZE_MAX / FDMA_PCI_MAX_MTU perform unsigned
>   arithmetic with no underflow guard and no BUILD_BUG_ON, even though…
> 
> Pre-existing issues:
> - [High] The hazard the new PCIe reload path documents and avoids is
>   still live in the sibling platform reload path, which this series…
> - [High] The two implementers of lan966x_fdma_ops::fdma_resize now
>   disagree about the 'rings not built yet' precondition, and the…
> - [Medium] Window in which DEV_MAC_MAXLEN_CFG exceeds the DCB buffer
>   size, combined with a missing SOF/EOF check in…
> 
> --
> 
> net: lan966x: add PCIe FDMA MTU change support
> 
> This adds MTU change support to the PCIe FDMA path by reallocating the
> contiguous ATU-mapped RX and TX rings at the new size and falling back to
> the old buffers on failure.  It also caps the PCIe DCB ring at 256 entries
> and advertises a derived dev->max_mtu (FDMA_PCI_MAX_MTU) when the FDMA is
> in use.
> 
> A few questions below about the new reload path and the new size macros.
> 
> > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c
> > index 5d6902459f20..940425beec2f 100644
> > --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c
> > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma_pci.c
> > @@ -358,7 +358,7 @@ static int lan966x_fdma_pci_init(struct lan966x *lan966x)
> >       lan966x->rx.lan966x = lan966x;
> >       lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x);
> >       rx_fdma->channel_id = FDMA_XTR_CHANNEL;
> > -     rx_fdma->n_dcbs = FDMA_DCB_MAX;
> > +     rx_fdma->n_dcbs = FDMA_PCI_DCB_MAX;
> >       rx_fdma->n_dbs = FDMA_RX_DCB_MAX_DBS;
> >       rx_fdma->priv = lan966x;
> >       rx_fdma->db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu);
> 
> [Severity: High]
> Can an MTU change race with ring construction here?  lan966x_fdma_pci_init()
> sets rx.lan966x before it fills in channel_id/n_dcbs/n_dbs/db_size/size and
> before the two sleeping allocations in lan966x_fdma_pci_rx_alloc() and
> lan966x_fdma_pci_tx_alloc().
> 
> The netdevs are already live at that point: lan966x_probe() runs the whole
> port loop, including lan966x_probe_port()->register_netdev() and
> lan966x_port_init()->lan966x_fdma_netdev_init()->netif_napi_add()+
> napi_enable(), and only calls ops->fdma_init() afterwards, without holding
> RTNL.
> 
> So an rtnl-held ndo_change_mtu in that window sees rx.lan966x != NULL, passes
> the new guard in lan966x_fdma_pci_resize(), finds a max_mtu that differs from
> the partially initialised rx.max_mtu, and enters __lan966x_fdma_pci_reload().
> Both contexts then write the same lan966x->rx.fdma / lan966x->tx.fdma:
> 
>   lan966x_fdma_pci_reload()
>       lan966x_fdma_rx_disable(&lan966x->rx)   /* channel_id still 0 */
>       lan966x->rx.fdma.atu_region = NULL;     /* under probe's feet */
>       lan966x_fdma_pci_rx_alloc(&lan966x->rx) /* second ring */
> 
> Does this leak probe's ring plus its ATU region (only
> FDMA_PCI_ATU_REGION_MAX == 6 exist), or trip WARN_ON(fdma->atu_region) /
> -ENOSPC in fdma_pci_atu_region_map()?  And on the failure branch,
> lan966x_fdma_pci_reset_mem() dereferences tx->fdma.atu_region->base_addr,
> which is NULL in a snapshot taken before probe allocated it.

Yes, same issue as adressed in #4. Theres a race because netdevs are registered
before initialization (in this case FDMA) has happened. The proper fix is a
reordering of the probe initialization.

> 
> [ ... ]
> 
> > @@ -391,9 +391,156 @@ static int lan966x_fdma_pci_init(struct lan966x *lan966x)
> >       return 0;
> >  }
> >
> > +/* Reset existing rx and tx buffers. */
> > +static void lan966x_fdma_pci_reset_mem(struct lan966x *lan966x)
> > +{
> > +     struct lan966x_rx *rx = &lan966x->rx;
> > +     struct lan966x_tx *tx = &lan966x->tx;
> > +
> > +     memset(rx->fdma.dcbs, 0, rx->fdma.size);
> > +     memset(tx->fdma.dcbs, 0, tx->fdma.size);
> > +
> > +     fdma_dcbs_init(&rx->fdma,
> > +                    FDMA_DCB_INFO_DATAL(rx->fdma.db_size - XDP_PACKET_HEADROOM),
> > +                    FDMA_DCB_STATUS_INTR);
> > +
> > +     fdma_dcbs_init(&tx->fdma,
> > +                    FDMA_DCB_INFO_DATAL(tx->fdma.db_size),
> > +                    FDMA_DCB_STATUS_DONE);
> > +
> > +     lan966x_fdma_llp_configure(lan966x,
> > +                                tx->fdma.atu_region->base_addr,
> > +                                tx->fdma.channel_id);
> > +     lan966x_fdma_llp_configure(lan966x,
> > +                                rx->fdma.atu_region->base_addr,
> > +                                rx->fdma.channel_id);
> > +}
> > +
> 
> [ ... ]
> 
> > +static int lan966x_fdma_pci_reload(struct lan966x *lan966x, int new_mtu)
> > +{
> > +     struct fdma tx_fdma_old = lan966x->tx.fdma;
> > +     struct fdma rx_fdma_old = lan966x->rx.fdma;
> > +     u32 old_mtu = lan966x->rx.max_mtu;
> > +     int err;
> > +
> > +     napi_disable(&lan966x->napi);
> > +     lan966x_fdma_tx_disable_netdev(lan966x);
> > +     lan966x_fdma_rx_disable(&lan966x->rx);
> > +     lan966x_fdma_tx_disable(&lan966x->tx);
> 
> [Severity: High]
> What happens if an MTU change lands after the PCIe FDMA has already been torn
> down?  lan966x_fdma_pci_deinit() disables NAPI and frees both coherent rings,
> but it leaves lan966x->rx.lan966x set, lan966x->fdma true and fdma->dcbs
> dangling.  It also runs before the netdevs are unregistered and without RTNL:
> 
>   lan966x_remove()
>       lan966x->ops->fdma_deinit(lan966x);
>       lan966x_cleanup_ports(lan966x);   /* unregister_netdev() here */
> 
> The new guard in lan966x_fdma_pci_resize() only rejects "rings not built
> yet", so an ndo_change_mtu in that window passes it and reaches this
> function.  Three things then follow in order:
> 
> napi_disable(&lan966x->napi) on an already-disabled NAPI.  Does
> napi_disable_locked() spin forever in
> 
>         while (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC))
>                 usleep_range(20, 200);
> 
> since nothing clears those bits until napi_enable()?  That task holds RTNL,
> so unregister_netdev() in lan966x_remove() can never complete.
> 
> If that is somehow passed, the success path below calls
> fdma_free_coherent_and_unmap() on the stale rx_fdma_old/tx_fdma_old
> snapshots, so dma_free_coherent(dev, fdma->size, fdma->dcbs, fdma->dma) runs
> a second time on the multi-MB blocks deinit already released.
> 
> And the restore: path calls lan966x_fdma_pci_reset_mem(), which memsets up to
> rx->fdma.size bytes into freed DMA memory and then dereferences
> tx->fdma.atu_region->base_addr, which fdma_free_coherent_and_unmap() already
> set to NULL.
> 
> Would a "rings are live" state check (or clearing rx.lan966x and dcbs in
> deinit, or unregistering the netdevs before fdma_deinit) close this?

lan966x_remove() is not touched by this series (apart from calling
ops->fdma_deinit), so I'd say pre-existing. But I believe this issue will also
disappear, when we refactor probe init as described above.

> 
> [Severity: Medium]
> Is the hardware guaranteed to be quiesced at this point?  Both
> lan966x_fdma_rx_disable() and lan966x_fdma_tx_disable() are void and swallow
> the result of
> 
>         readx_poll_timeout_atomic(lan966x_fdma_channel_active, lan966x,
>                                   val, !(val & BIT(fdma->channel_id)),
>                                   READL_SLEEP_US, READL_TIMEOUT_US);
> 
> so a -ETIMEDOUT never reaches this caller, and the EQ_AVAIL drain poll in
> __lan966x_fdma_pci_reload() is discarded too (see below).  If either poll
> times out, the extraction/injection engine still owns the old DCB ring while
> this function reprograms FDMA_DCB_LLP and then calls
> fdma_free_coherent_and_unmap(&rx_fdma_old), which does:
> 
>   fdma_free_coherent_and_unmap()
>       fdma_pci_atu_region_unmap(fdma->atu_region);  /* ENA=0, translation cleared */
>       fdma->atu_region = NULL;
>       fdma_free_coherent(dev, fdma);                /* pages returned */
> 
> On the PCIe path the ATU window is the address translation itself, so
> in-flight writes land on a disabled outbound window or into memory already
> handed back to the DMA allocator.  Should a timeout abort the resize and
> propagate the error so the MTU change is rejected instead?

I believe the quiesce poll before teardown is also discarded on lan966x, lan969x
and sparx5 alltogether, same pattern.  READL_TIMEOUT_US is 100 seconds, so a
timeout means the channel was already wedged for a long time.

> 
> > +
> > +     lan966x->rx.max_mtu = new_mtu;
> > +
> > +     /* Must be NULL'ed in order to realloc them. */
> > +     lan966x->rx.fdma.atu_region = NULL;
> > +     lan966x->tx.fdma.atu_region = NULL;
> > +
> > +     lan966x->tx.fdma.db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu);
> > +     lan966x->tx.fdma.size = fdma_get_size_contiguous(&lan966x->tx.fdma);
> > +     lan966x->rx.fdma.db_size = FDMA_PCI_DB_SIZE(lan966x->rx.max_mtu);
> > +     lan966x->rx.fdma.size = fdma_get_size_contiguous(&lan966x->rx.fdma);
> > +
> > +     err = lan966x_fdma_pci_rx_alloc(&lan966x->rx);
> > +     if (err)
> > +             goto restore;
> > +
> > +     err = lan966x_fdma_pci_tx_alloc(&lan966x->tx);
> > +     if (err) {
> > +             fdma_free_coherent_and_unmap(lan966x->dma_dev,
> > +                                          &lan966x->rx.fdma);
> > +             goto restore;
> > +     }
> > +
> > +     /* Free and unmap old memory. */
> > +     fdma_free_coherent_and_unmap(lan966x->dma_dev, &rx_fdma_old);
> > +     fdma_free_coherent_and_unmap(lan966x->dma_dev, &tx_fdma_old);
> > +
> > +     /* Order matters: napi_enable() must precede the wakes, or a TX that
> > +      * completes first clears FDMA_INTR_DB_ENA with nothing scheduled to
> > +      * restore it, leaving RX dead until the next reload.
> > +      */
> > +     napi_enable(&lan966x->napi);
> > +     lan966x_fdma_rx_start(&lan966x->rx);
> > +     lan966x_fdma_pci_wakeup_netdev(lan966x);
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but the hazard this comment
> describes looks like it is still live in the sibling register-path reload,
> which the series leaves untouched and the commit message does not mention.
> 
> lan966x_fdma_reload() uses exactly the order this comment forbids, in both
> its success and its restore path:
> 
>         lan966x_fdma_rx_start(&lan966x->rx);    /* re-arms RX bit in DB_ENA */
>         ... page_pool_put_full_page() over 512 DCBs ...
>         fdma_free_coherent();
>         page_pool_destroy();
>         lan966x_fdma_wakeup_netdev(lan966x);
>         napi_enable(&lan966x->napi);
> 
> The mechanism matches what the comment states.  lan966x_fdma_irq_handler()
> does:
> 
>         if (db) {
>                 lan_wr(0, lan966x, FDMA_INTR_DB_ENA);
>                 lan_wr(db, lan966x, FDMA_INTR_DB);
> 
>                 napi_schedule(&lan966x->napi);
>         }
> 
> With NAPI disabled, napi_schedule_prep() returns false and only sets
> NAPIF_STATE_MISSED; napi_enable_locked() clears SCHED/NPSVC but does not
> re-check MISSED, so no poll runs and the only code that restores DB_ENA
> (the lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA) after napi_complete_done()) is
> never reached.  Since DB_ENA is now 0, no further DB interrupt can be raised
> either.
> 
> The TX channel is not disabled during that window, so an in-flight TX
> completion or RX DB interrupt there appears to mask all FDMA interrupts
> permanently until another MTU change happens to reload.  Should the platform
> path get the same ordering fix?
> 
> > +
> > +     return err;
> > +restore:
> > +
> > +     /* No new buffers are allocated at this point. Use the old buffers,
> > +      * but reset them before starting the FDMA again.
> > +      */
> > +
> > +     memcpy(&lan966x->tx.fdma, &tx_fdma_old, sizeof(struct fdma));
> > +     memcpy(&lan966x->rx.fdma, &rx_fdma_old, sizeof(struct fdma));
> > +
> > +     lan966x->rx.max_mtu = old_mtu;
> > +
> > +     lan966x_fdma_pci_reset_mem(lan966x);
> > +
> > +     napi_enable(&lan966x->napi);
> > +     lan966x_fdma_rx_start(&lan966x->rx);
> > +     lan966x_fdma_pci_wakeup_netdev(lan966x);
> > +
> > +     return err;
> > +}
> 
> [Severity: Medium]
> This is a pre-existing ordering issue, but does this patch widen it
> noticeably?  lan966x_port_change_mtu() programs the new frame length before
> the buffers are grown:
> 
>         lan_wr(DEV_MAC_MAXLEN_CFG_MAX_LEN_SET(LAN966X_HW_MTU(new_mtu)),
>                lan966x, DEV_MAC_MAXLEN_CFG(port->chip_port));
>         WRITE_ONCE(dev->mtu, new_mtu);
> 
>         if (!lan966x->fdma)
>                 return 0;
> 
>         err = lan966x->ops->fdma_resize(lan966x);
> 
> and only restores the old value after the callback returns.  On the PCIe path
> the callback now does real work: the CPU-queue flush, a
> usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC), two multi-megabyte coherent
> allocations, and on this restore: path a full RX/TX restart with db_size and
> DATAL back at the old smaller value while the larger MAXLEN is still
> programmed.
> 
> With n_dbs == 1 the FDMA bounds each write by DATAL but continues an
> oversized frame in the next DCB, and lan966x_fdma_pci_rx_check_frame() only
> validates BLOCKL:
> 
>         blockl = FDMA_DCB_STATUS_BLOCKL(db->status);
>         if (!lan966x_fdma_pci_rx_size_fits(fdma, blockl))
>                 return FDMA_ERROR;
> 
> It never checks FDMA_DCB_STATUS_SOF/EOF, so a continuation DB is treated as a
> complete frame and lan966x_ifh_get_src_port(virt_addr, src_port) parses
> payload bytes as the IFH source port.  src_port is range-checked and data_len
> is bounded, so this is not an out-of-bounds access, but can a remote sender
> that injects an oversized frame in this window get frames delivered to the
> stack attributed to a source port of its choosing?
> 
> > +
> > +static int __lan966x_fdma_pci_reload(struct lan966x *lan966x, int max_mtu)
> > +{
> > +     int err;
> > +     u32 val;
> > +
> > +     /* Disable the CPU port. */
> > +     lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0),
> > +             QSYS_SW_PORT_MODE_PORT_ENA,
> > +             lan966x, QSYS_SW_PORT_MODE(CPU_PORT));
> > +
> > +     /* Flush the CPU queues. */
> > +     readx_poll_timeout(lan966x_qsys_sw_status,
> > +                        lan966x,
> > +                        val,
> > +                        !(QSYS_SW_STATUS_EQ_AVAIL_GET(val)),
> > +                        READL_SLEEP_US, READL_TIMEOUT_US);
> 
> [Severity: Medium]
> The return value of this readx_poll_timeout() is discarded, so "CPU queues
> drained" is assumed rather than verified before the rings are retired in
> lan966x_fdma_pci_reload().  Should a -ETIMEDOUT here abort the resize?
> 
> > +
> > +     /* Add a sleep in case there are frames between the queues and the CPU
> > +      * port
> > +      */
> > +     usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
> > +
> > +     err = lan966x_fdma_pci_reload(lan966x, max_mtu);
> > +
> > +     /* Enable back the CPU port. */
> > +     lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1),
> > +             QSYS_SW_PORT_MODE_PORT_ENA,
> > +             lan966x, QSYS_SW_PORT_MODE(CPU_PORT));
> > +
> > +     return err;
> > +}
> > +
> >  static int lan966x_fdma_pci_resize(struct lan966x *lan966x)
> >  {
> > -     return -EOPNOTSUPP;
> > +     int max_mtu;
> > +
> > +     /* Nothing to resize until fdma_pci_init() has built the rings; it
> > +      * sizes them from DEV_MAC_MAXLEN_CFG, which the caller already set.
> > +      */
> > +     if (!lan966x->rx.lan966x)
> > +             return 0;
> 
> [Severity: High]
> This is a pre-existing gap in the other callback rather than something this
> patch introduces, but the two implementers of
> lan966x_fdma_ops::fdma_resize now disagree about this precondition.  The
> platform one has no equivalent check:
> 
> lan966x_fdma.c:lan966x_fdma_change_mtu() {
>         max_mtu = lan966x_fdma_get_max_frame(lan966x);
>         if (max_mtu == lan966x->rx.max_mtu)
>                 return 0;
> 
>         return __lan966x_fdma_reload(lan966x, max_mtu);
> }
> 
> lan966x_fdma_init() sets rx.lan966x = lan966x only at its own start, so in
> the window between register_netdev() in lan966x_probe_port() and
> ops->fdma_init() in lan966x_probe(), rx.max_mtu is 0, the comparison always
> differs, and the reload runs against an unbuilt ring:
> 
>   __lan966x_fdma_reload()->lan966x_fdma_reload()
>       lan966x_fdma_rx_disable(&lan966x->rx)
>           struct lan966x *lan966x = rx->lan966x;   /* NULL */
>           lan_rmw(..., lan966x, FDMA_CH_DISABLE);  /* lan966x->regs */
> 
> lan966x->fdma is already true before the port loop and the NAPI instance is
> added and enabled by lan966x_port_init()->lan966x_fdma_netdev_init(), so
> nothing else stops the path.  Would the guard be better placed once in the
> shared caller lan966x_port_change_mtu(), or duplicated in both callbacks?
> 
> > +
> > +     max_mtu = lan966x_fdma_get_max_frame(lan966x);
> > +     if (max_mtu == lan966x->rx.max_mtu)
> > +             return 0;
> > +
> > +     return __lan966x_fdma_pci_reload(lan966x, max_mtu);
> >  }
> >
> >  static void lan966x_fdma_pci_deinit(struct lan966x *lan966x)
> > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
> > index c803619d83e2..2177e2bbfbd3 100644
> > --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
> > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
> > @@ -823,7 +823,8 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
> >       port->chip_port = p;
> >       lan966x->ports[p] = port;
> >
> > -     dev->max_mtu = ETH_MAX_MTU;
> > +     dev->max_mtu = lan966x_is_pci(lan966x) && lan966x->fdma ?
> > +                    FDMA_PCI_MAX_MTU : ETH_MAX_MTU;
> >
> >       dev->netdev_ops = &lan966x_port_netdev_ops;
> >       dev->ethtool_ops = &lan966x_ethtool_ops;
> > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> > index 16bc28c8f11f..1877f1916d71 100644
> > --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
> 
> [ ... ]
> 
> > @@ -87,6 +88,33 @@
> >  #define FDMA_INJ_CHANNEL             0
> >  #define FDMA_DCB_MAX                 512
> >
> > +/* Ring must fit in one MAX_PAGE_ORDER DMA block; 512 DCBs overflows
> > + * at jumbo MTU.
> > + */
> > +#define FDMA_PCI_DCB_MAX             256
> > +
> > +#define FDMA_OVERHEAD                                                        \
> > +     (IFH_LEN_BYTES +                                                \
> > +      SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +               \
> > +      VLAN_HLEN * 2 +                                                \
> > +      XDP_PACKET_HEADROOM)
> > +
> > +/* Largest db_size keeping the ATU-padded ring inside one MAX_PAGE_ORDER
> > + * block and within the 16-bit DCB DATAL field. Inverts ALIGN(x, R) <= L
> > + * into x <= ALIGN_DOWN(L, R) to bound x directly.
> > + */
> > +#define FDMA_PCI_DB_SIZE_MAX                                         \
> > +     MIN_T(u32,                                                      \
> > +           (ALIGN_DOWN(PAGE_SIZE << MAX_PAGE_ORDER,                  \
> > +                       FDMA_PCI_ATU_REGION_ALIGN) -                  \
> > +            FDMA_PCI_DCB_MAX * sizeof(struct fdma_dcb)) /            \
> > +           (FDMA_PCI_DCB_MAX * FDMA_RX_DCB_MAX_DBS),                 \
> > +           ALIGN_DOWN(GENMASK(15, 0), FDMA_PCI_DB_ALIGN))
> > +
> > +#define FDMA_PCI_MAX_MTU                                             \
> > +     (FDMA_PCI_DB_SIZE_MAX - FDMA_OVERHEAD -                         \
> > +      (ETH_HLEN + ETH_FCS_LEN))
> 
> [Severity: Low]
> Would a BUILD_BUG_ON help here?  Both subtractions are unsigned and
> unguarded, and this macro is the only enforcement point for the
> MAX_PAGE_ORDER ring-size limit, the 16-bit DATAL limit, and dev->max_mtu.
> 
> FDMA_PCI_DCB_MAX * sizeof(struct fdma_dcb) is 256 * 256 = 65536.  If
> PAGE_SIZE << MAX_PAGE_ORDER is at or below 128 KB, the first subtraction
> wraps and MIN_T(u32, ...) then picks the 65408 branch.  In between, the
> second subtraction wraps: with 4 KB pages and MAX_PAGE_ORDER == 5 the
> quotient is 256 while FDMA_OVERHEAD + ETH_HLEN + ETH_FCS_LEN is 630, so
> FDMA_PCI_MAX_MTU becomes roughly 4G and is assigned straight into
> dev->max_mtu, so dev_validate_mtu() accepts anything up to INT_MAX and the
> cap this patch installs disappears.
> 
> In the other direction, 4 KB pages with MAX_PAGE_ORDER 6 or 7 yield 138 and
> 1162, i.e. dev->max_mtu below ETH_DATA_LEN, so a plain 1500-byte MTU cannot
> be set.  Nothing asserts FDMA_PCI_MAX_MTU >= ETH_DATA_LEN.
> 
> The default 4 KB-page, MAX_PAGE_ORDER=10 build does give 16128 and MTU 15498
> as the commit message says, and arch/arm/Kconfig declares ARCH_FORCE_MAX_ORDER
> as a plain int prompt with no range constraint (it already ships
> default "8" if SA1111), so these values are at least legal.
> 
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260918-lan966x-pci-fdma-v7-0-0ecc179c8a2c%40microchip.com



More information about the linux-arm-kernel mailing list