[PATCH net-next v7 04/14] net: lan966x: add FDMA LLP register write helper
Daniel Machon
daniel.machon at microchip.com
Wed Sep 23 00:07:47 PDT 2026
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider.
>
> Critical: 0 · High: 0 (1) · Medium: 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).
>
> - [Medium] By moving the FDMA_DCB_LLP/LLP1 writes out of
> lan966x_fdma_tx_activate() into lan966x_fdma_tx_alloc()…
> - [Low] The patch deletes the comment 'When activating a channel, first
> is required to write the first DCB address and then to activate it'…
>
> Pre-existing issues:
> - [High] lan966x_fdma_rx_alloc() (lan966x_fdma.c:135) and
> lan966x_fdma_tx_alloc() (line 217) discard the return value of…
>
> --
>
> net: lan966x: add FDMA LLP register write helper
>
> This moves the FDMA_DCB_LLP/LLP1 register writes out of
> lan966x_fdma_rx_start() and lan966x_fdma_tx_activate() into the RX/TX
> allocation functions, adding a shared lan966x_fdma_llp_configure() helper
> plus one extra call in the lan966x_fdma_reload() restore path. The stated
> motivation is that the upcoming PCIe FDMA path programs ATU-translated
> addresses into these registers.
>
> A few questions below.
>
> > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
> > index 41d4ec7f2f57..b8344fd5e5ad 100644
> > --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
> > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
> > @@ -109,6 +109,13 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx)
> > return 0;
> > }
> >
> > +static void lan966x_fdma_llp_configure(struct lan966x *lan966x, u64 addr,
> > + u8 channel_id)
> > +{
> > + lan_wr(lower_32_bits(addr), lan966x, FDMA_DCB_LLP(channel_id));
> > + lan_wr(upper_32_bits(addr), lan966x, FDMA_DCB_LLP1(channel_id));
> > +}
> > +
> > static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx)
> > {
> > struct lan966x *lan966x = rx->lan966x;
> > @@ -128,6 +135,8 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx)
> > fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size),
> > FDMA_DCB_STATUS_INTR);
> >
> > + lan966x_fdma_llp_configure(lan966x, fdma->dma, fdma->channel_id);
> > +
> > return 0;
> > }
>
> [Severity: High]
> This isn't a bug introduced by this patch, but since the new LLP write now
> sits right after it: should lan966x_fdma_rx_alloc() check the return value
> of fdma_dcbs_init() before arming the channel?
>
> fdma_dcbs_init() is fallible and stops at the first failing descriptor:
>
> drivers/net/ethernet/microchip/fdma/fdma_api.c:fdma_dcbs_init() {
> for (i = 0; i < fdma->n_dcbs; i++) {
> err = fdma_dcb_add(fdma, i, info, status);
> if (err)
> return err;
> }
> }
>
> and lan966x_fdma_rx_dataptr_cb() returns -ENOMEM when
> page_pool_dev_alloc_pages() fails, without updating rx->page[dcb][db] or
> *dataptr. So under memory pressure the chain is left truncated, the
> remaining DB dataptrs stay 0, and rx->page[] keeps its previous contents
> while this function still returns 0 and now also programs the LLP.
>
> In lan966x_fdma_reload() the success branch then runs, which releases every
> page in old_pages to the old pool and calls page_pool_destroy(page_pool)
> while rx->page[] still references those pages. A later
> lan966x_fdma_rx_free_pages() at deinit, or on the next MTU change, would
> then call page_pool_put_full_page() on pages already returned to a
> destroyed pool. In the probe path the same suppressed failure leaves NULL
> entries in rx->page[] that lan966x_fdma_rx_free_pages() dereferences
> unconditionally.
>
> RX would also stall, since the software dcb_index wraps modulo n_dcbs while
> the hardware only walks the truncated part of the ring.
>
> Interestingly the PCIe variant added later in this series does check the
> same call, so would it make sense to propagate the error here as well?
>
> > @@ -137,14 +146,6 @@ static void lan966x_fdma_rx_start(struct lan966x_rx *rx)
> > struct fdma *fdma = &rx->fdma;
> > u32 mask;
> >
> > - /* When activating a channel, first is required to write the first DCB
> > - * address and then to activate it
> > - */
> > - lan_wr(lower_32_bits((u64)fdma->dma), lan966x,
> > - FDMA_DCB_LLP(fdma->channel_id));
> > - lan_wr(upper_32_bits((u64)fdma->dma), lan966x,
> > - FDMA_DCB_LLP1(fdma->channel_id));
> > -
> > lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
> > FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
> > FDMA_CH_CFG_CH_INJ_PORT_SET(0) |
>
> [Severity: Low]
> This isn't a bug, but the comment documenting the hardware ordering rule
> ("first is required to write the first DCB address and then to activate
> it") is removed from both lan966x_fdma_rx_start() and
> lan966x_fdma_tx_activate(), and is not restated at
> lan966x_fdma_llp_configure() or at either FDMA_CH_ACTIVATE site.
>
> The requirement becomes an implicit contract between the allocation and
> activation functions, which is what forces the extra open-coded call in the
> lan966x_fdma_reload() restore path below. Could the rule be documented at
> the helper, or near the FDMA_CH_ACTIVATE writes?
>
> All activation paths in the current tree do have a matching LLP write
> before them, so this is only about the lost documentation.
>
> Also, the commit message says the writes are moved "into the allocation
> functions", but a third call site is added in lan966x_fdma_reload(), which
> is not an allocation function. Could that be mentioned as well?
>
> > @@ -215,6 +216,8 @@ static int lan966x_fdma_tx_alloc(struct lan966x_tx *tx)
> >
> > fdma_dcbs_init(fdma, 0, 0);
> >
> > + lan966x_fdma_llp_configure(lan966x, fdma->dma, fdma->channel_id);
> > +
> > return 0;
> >
> > out:
>
> [Severity: High]
> This is the same pre-existing unchecked fdma_dcbs_init() described above,
> on the TX side; it is not introduced by this patch, but the new LLP write
> and the unconditional "return 0" now follow it directly.
>
> > @@ -236,14 +239,6 @@ static void lan966x_fdma_tx_activate(struct lan966x_tx *tx)
> > struct fdma *fdma = &tx->fdma;
> > u32 mask;
> >
> > - /* When activating a channel, first is required to write the first DCB
> > - * address and then to activate it
> > - */
> > - lan_wr(lower_32_bits((u64)fdma->dma), lan966x,
> > - FDMA_DCB_LLP(fdma->channel_id));
> > - lan_wr(upper_32_bits((u64)fdma->dma), lan966x,
> > - FDMA_DCB_LLP1(fdma->channel_id));
> > -
> > lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
> > FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
> > FDMA_CH_CFG_CH_INJ_PORT_SET(0) |
>
> [Severity: Medium]
> Can the injection channel now be activated before the LLP has been
> programmed during probe?
>
> lan966x_probe() sets lan966x->fdma = true before the port loop that calls
> lan966x_probe_port() -> register_netdev(), and only calls
> lan966x_fdma_init() afterwards. lan966x_port_xmit() dispatches purely on
> that flag:
>
> drivers/net/ethernet/microchip/lan966x/lan966x_main.c:lan966x_port_xmit() {
> spin_lock(&lan966x->tx_lock);
> if (port->lan966x->fdma)
> err = lan966x_fdma_xmit(skb, ifh, dev);
> ...
> }
>
> lan966x_fdma_init() sets tx.fdma.n_dcbs/n_dbs and the ops before calling
> lan966x_fdma_tx_alloc(), and lan966x_fdma_tx_alloc() does not take
> tx_lock. So an xmit landing between fdma_dcbs_init() and the new
> lan966x_fdma_llp_configure() would find a valid dcbs_buf and DCB ring and
> go:
>
> lan966x_fdma_xmit() -> lan966x_fdma_tx_start() -> lan966x_fdma_tx_activate()
>
> which after this patch writes FDMA_CH_ACTIVATE without programming the
> LLP, so the engine would fetch its first DCB from the post-reset LLP value
> of 0. tx->activated is latched true there, so the LLP would never be
> programmed for that channel afterwards:
>
> if (likely(lan966x->tx.activated)) {
> lan966x_fdma_tx_reload(tx);
> } else {
> /* Because it is first time, then just activate */
> lan966x->tx.activated = true;
> lan966x_fdma_tx_activate(tx);
> }
>
> Before this patch tx_activate() wrote the LLP immediately before the
> activate bit, so this ordering was not possible. Note the same probe
> window already exposes a pre-existing tx->dcbs_buf == NULL dereference in
> lan966x_fdma_get_next_dcb() while n_dcbs is already set, so would
> registering the netdevs after lan966x_fdma_init() be the better fix?
Netdev registration should be done last, yes - and that is the fix that should
go to net (as mentioned in v6). The probe window is real, but not something
introduced by this series.
>
> > @@ -876,6 +871,9 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu)
> > MEM_TYPE_PAGE_POOL, page_pool);
> > }
> >
> > + lan966x_fdma_llp_configure(lan966x, lan966x->rx.fdma.dma,
> > + lan966x->rx.fdma.channel_id);
> > +
> > lan966x_fdma_rx_start(&lan966x->rx);
> >
> > lan966x_fdma_wakeup_netdev(lan966x);
>
> --
> 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