Netdev List
 help / color / mirror / Atom feed
* RE: [PATCH net-next v10 5/5] net: wangxun: add pcie error handler
From: Jiawen Wu @ 2026-07-16  6:55 UTC (permalink / raw)
  To: 'Simon Horman'
  Cc: netdev, mengyuanlou, andrew+netdev, davem, edumazet, kuba, pabeni,
	richardcochran, linux, aleksandr.loktionov, jacob.e.keller,
	michal.swiatkowski, kees, joe, leitao, shaojijie, u.kleine-koenig,
	weirongguang, larysa.zaremba, fabio.baltieri
In-Reply-To: <20260714091129.1815871-1-horms@kernel.org>

On Tue, Jul 14, 2026 5:11 PM, Simon Horman wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> --- Patch [5]: [PATCH net-next v10 5/5] net: wangxun: add pcie error handler ---
> commit 91baa4e77d07ab6772569a7d37ed86cfda625c00
> Author: Jiawen Wu <jiawenwu@trustnetic.com>
> Subject: net: wangxun: add pcie error handler
> 
> This commit adds PCIe error handling support (AER) to the wangxun drivers
> (ngbe and txgbe). It detects hardware errors, attempts to soft quiesce,
> and registers standard PCI error handler callbacks.
> 
> > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > index ee27f96735dc7..0cb99e456a1c9 100644
> > --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> 
> [ ... ]
> 
> > +static void wx_io_resume(struct pci_dev *pdev)
> > +{
> > +	struct wx *wx = pci_get_drvdata(pdev);
> > +	struct net_device *netdev;
> > +	int err;
> > +
> > +	netdev = wx->netdev;
> > +	rtnl_lock();
> > +	if (netif_running(netdev)) {
> > +		err = netdev->netdev_ops->ndo_open(netdev);
> 
> [Severity: High]
> Does this sequence risk corrupting the hw_tc_lock seqlock?
> 
> During AER recovery, wx_io_slot_reset() calls down_suspend(), which bypasses
> wx_ptp_stop(). This leaves the PTP clock registered and potentially in use by
> userspace.
> 
> When wx_io_resume() then calls ndo_open(), this triggers txgbe_up() or
> ngbe_up(), which in turn calls wx_ptp_init(). Since wx_ptp_init()
> unconditionally calls seqlock_init(&wx->hw_tc_lock), won't this corrupt the
> lock if userspace is actively issuing PTP ioctls?

PTP clock was destroyed in wx_ptp_quiesce() before V8 patch set.
The piece of code was removed based on the Sashiko's comment.
I think it should be added back because the order of the calls in wx_soft_quiesce()
has been changed.

> 
> > +		if (err) {
> > +			wx_err(wx, "Failed to open netdev after reset\n");
> 
> [ ... ]
> 
> > diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > index 92895f5035118..56d4b63387fd6 100644
> > --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> 
> [ ... ]
> 
> > @@ -413,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)
> >
> >  static void ngbe_reset(struct wx *wx)
> >  {
> > +	if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
> > +		return;
> > +
> >  	wx_flush_sw_mac_table(wx);
> 
> [Severity: High]
> Is the hardware properly reinitialized here after an AER slot reset?
> 
> In wx_io_slot_reset(), the hardware needs to be restarted from scratch.
> While txgbe_reset() calls txgbe_reset_hw(), ngbe_reset() only flushes the
> software MAC tables and skips calling ngbe_reset_hw().
> 
> Because this patch registers wx_err_handler for the ngbe driver, won't this
> leave the hardware uninitialized and the interface broken after a PCIe slot
> reset?

I think I have answered this question many times and also described ngbe's lack
of functionality in the commit and cover-letter messages.

> 
> [ ... ]
> 
> > @@ -529,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)
> >  {
> >  	struct wx *wx = netdev_priv(netdev);
> >
> > +	if (test_bit(WX_STATE_RES_FREED, wx->state))
> > +		goto out;
> > +
> >  	wx_ptp_stop(wx);
> 
> [Severity: High]
> Does this early return cause a use-after-free for the PTP clock?
> 
> If a PCI error occurs, down_suspend() sets WX_STATE_RES_FREED but
> intentionally skips wx_ptp_stop(). If the interface is subsequently closed or
> unregistered, ngbe_close() will return early here and never call
> wx_ptp_stop().
> 
> Because the PTP clock device is never unregistered, it remains exposed to
> userspace after the driver's memory is freed. Can this lead to a
> use-after-free if userspace interacts with the clock later?
> 
> [ ... ]
> 
> > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> > index a7bde03a98feb..d85ee83192e4f 100644
> > --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> > +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> 
> [ ... ]
> 
> > @@ -530,12 +549,16 @@ static int txgbe_close(struct net_device *netdev)
> >  {
> >  	struct wx *wx = netdev_priv(netdev);
> >
> > +	if (test_bit(WX_STATE_RES_FREED, wx->state))
> > +		goto out;
> > +
> >  	wx_ptp_stop(wx);
> 
> [Severity: High]
> Similar to ngbe_close() above, does this early return skip wx_ptp_stop() and
> lead to a use-after-free if the txgbe device is closed after an AER suspend?
> 


^ permalink raw reply

* [PATCH net-next] net: stmmac: dwmac-rk: enable the reference clock for output mode
From: Jiaxing Hu @ 2026-07-16  6:56 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, heiko,
	mcoquelin.stm32, alexandre.torgue
  Cc: netdev, linux-rockchip, linux-arm-kernel, linux-stm32,
	linux-kernel


rk_gmac_clk_init() only requests the refout clock group for RMII. The
ArmSoM CM5 has an on-module YT8531 RGMII PHY with no crystal that needs
the SoC 25 MHz reference (clk_mac_refout), so in RGMII the clock was
never enabled and the PHY did not respond on MDIO.

Request the group whenever the SoC drives the clock (clock_in_out =
"output"), not just for RMII. The clocks are optional, so other boards
are unaffected.

Signed-off-by: Jiaxing Hu <gahing@gahingwoo.com>
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index 8d7042e68..dd060e4b8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -1112,7 +1112,7 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
 	bsp_priv->clk_enabled = false;
 
 	bsp_priv->num_clks = ARRAY_SIZE(rk_clocks);
-	if (phy_iface == PHY_INTERFACE_MODE_RMII)
+	if (phy_iface == PHY_INTERFACE_MODE_RMII || !bsp_priv->clock_input)
 		bsp_priv->num_clks += ARRAY_SIZE(rk_rmii_clocks);
 
 	bsp_priv->clks = devm_kcalloc(dev, bsp_priv->num_clks,
@@ -1123,7 +1123,7 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
 	for (i = 0; i < ARRAY_SIZE(rk_clocks); i++)
 		bsp_priv->clks[i].id = rk_clocks[i];
 
-	if (phy_iface == PHY_INTERFACE_MODE_RMII) {
+	if (phy_iface == PHY_INTERFACE_MODE_RMII || !bsp_priv->clock_input) {
 		for (j = 0; j < ARRAY_SIZE(rk_rmii_clocks); j++)
 			bsp_priv->clks[i++].id = rk_rmii_clocks[j];
 	}
-- 
2.43.0




^ permalink raw reply related

* [PATCH 2/2] sctp: auth: Fix safety issue when skb_clone fails in auth_chunk handling
From: luoqing @ 2026-07-16  6:52 UTC (permalink / raw)
  To: marcelo.leitner, lucien.xin, davem, edumazet, kuba, pabeni
  Cc: horms, linux-sctp, netdev, linux-kernel, luoqing
In-Reply-To: <20260716065259.872235-1-l1138897701@163.com>

From: luoqing <luoqing@kylinos.cn>

When processing AUTH + COOKIE-ECHO packets, if skb_clone fails due to
memory pressure, chunk->auth_chunk is set to NULL but chunk->auth is
still set to 1. This causes sctp_auth_chunk_verify to skip the AUTH
validation (since auth_chunk is NULL), allowing unauthenticated
COOKIE-ECHO packets to be accepted.

Fix this by only setting chunk->auth = 1 when skb_clone succeeds.

Signed-off-by: luoqing <luoqing@kylinos.cn>
---
 net/sctp/associola.c   | 3 ++-
 net/sctp/endpointola.c | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 62d3cc155809..e54068305396 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -999,7 +999,8 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
 			if (next_hdr->type == SCTP_CID_COOKIE_ECHO) {
 				chunk->auth_chunk = skb_clone(chunk->skb,
 							      GFP_ATOMIC);
-				chunk->auth = 1;
+				if (chunk->auth_chunk)
+					chunk->auth = 1;
 				continue;
 			}
 		}
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index dfb1719275db..3419748c66bc 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -368,7 +368,8 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work)
 			if (next_hdr->type == SCTP_CID_COOKIE_ECHO) {
 				chunk->auth_chunk = skb_clone(chunk->skb,
 								GFP_ATOMIC);
-				chunk->auth = 1;
+				if (chunk->auth_chunk)
+					chunk->auth = 1;
 				continue;
 			}
 		}
-- 
2.25.1


^ permalink raw reply related

* [PATCH 1/2] sctp: socket: Fix uninitialized error on socket shutdown
From: luoqing @ 2026-07-16  6:52 UTC (permalink / raw)
  To: marcelo.leitner, lucien.xin, davem, edumazet, kuba, pabeni
  Cc: horms, linux-sctp, netdev, linux-kernel, luoqing

From: luoqing <luoqing@kylinos.cn>

When sctp_skb_recv_datagram() detects sk->sk_shutdown & RCV_SHUTDOWN,
it breaks out of the loop and returns NULL without setting *err.
This leaves the error pointer uninitialized or with a stale value,
which can confuse callers expecting a clean shutdown indication.

Compare with the generic __skb_wait_for_more_packets() in
net/core/datagram.c which properly handles shutdown by setting *err = 0.

Fix this by setting *err = 0 before breaking when the socket is shut down,
indicating an orderly shutdown rather than an error condition.

Signed-off-by: luoqing <luoqing@kylinos.cn>
---
 net/sctp/socket.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c7b9e325ec1c..ea7050b27715 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -9117,8 +9117,10 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
 		if (error)
 			goto no_packet;
 
-		if (sk->sk_shutdown & RCV_SHUTDOWN)
+		if (sk->sk_shutdown & RCV_SHUTDOWN) {
+			*err = 0;
 			break;
+		}
 
 
 		/* User doesn't want to wait.  */
-- 
2.25.1


^ permalink raw reply related

* [PATCH ethtool 8/8] cmis: Fix printing of link length
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

The CMIS parser only supports link length multipliers of 0.1 and 1, but
revision 5.0 of the specification (May 2021) added a multiplier of 10
and revision 5.3 (September 2024) added multipliers of 50, 100, 200 and
500.

Add support for the missing multipliers.

Fixes: 88ca347ef35a ("Add QSFP-DD support")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c | 8 +++++++-
 cmis.h | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/cmis.c b/cmis.c
index 351e18484854..7a7e928ebb2f 100644
--- a/cmis.c
+++ b/cmis.c
@@ -188,6 +188,7 @@ static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map)
  */
 static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map)
 {
+	static const float smf_mul2[] = { 50.0f, 100.0f, 200.0f, 500.0f };
 	static const char *fn = "Length (SMF)";
 	float mul = 1.0f;
 	float val = 0.0f;
@@ -203,7 +204,12 @@ static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map)
 	case CMIS_MULTIPLIER_01:
 		mul = 1.0f;
 		break;
-	default:
+	case CMIS_MULTIPLIER_10:
+		mul = 10.0f;
+		break;
+	case CMIS_MULTIPLIER_11:
+		mul = smf_mul2[(map->page_01h[CMIS_SMF_LEN_MUL2_OFFSET] &
+				CMIS_LEN_MUL_MASK) >> 6];
 		break;
 	}
 
diff --git a/cmis.h b/cmis.h
index 82fd2456a3ec..387809e00cd5 100644
--- a/cmis.h
+++ b/cmis.h
@@ -120,6 +120,7 @@
 #define CMIS_OM4_LEN_OFFSET			0x86
 #define CMIS_OM3_LEN_OFFSET			0x87
 #define CMIS_OM2_LEN_OFFSET			0x88
+#define CMIS_SMF_LEN_MUL2_OFFSET		0x89
 
 /* Wavelength (Page 1) */
 #define CMIS_NOM_WAVELENGTH_MSB			0x8A
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 7/8] cmis: Fix printing of attenuation and wavelength
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

The CMIS parser uses the same trick as the SFF-8636 parser to identify
copper cables for which attenuation should be printed. That is, it
considers every transmitter technology value above 0x09 as an indication
that it is dealing with a copper cable. This is not true for CMIS
transceivers which support more transmitter technologies compared to
SFF-8636. For example, according to table 8-40 in CMIS 5.3, 0x10 is
"C-band tunable laser".

Fix the identification of copper cables by only matching on the relevant
transmitter technology values. For all the rest, only print the
wavelength if Page 01h is supported and the nominal wavelength is not 0.

Fixes: 88ca347ef35a ("Add QSFP-DD support")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/cmis.c b/cmis.c
index 19a3eb7cf925..351e18484854 100644
--- a/cmis.c
+++ b/cmis.c
@@ -255,10 +255,20 @@ static void cmis_show_sig_integrity(const struct cmis_memory_map *map)
 static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
 {
 	__u8 value = map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET];
+	float wl, wl_tol;
 
 	module_show_mit_compliance(value);
 
-	if (value >= MODULE_TT_COPPER_UNEQUAL) {
+	switch (value) {
+	case MODULE_TT_COPPER_UNEQUAL:
+	case MODULE_TT_COPPER_PASS_EQUAL:
+	case MODULE_TT_COPPER_NF_EQUAL:
+	case MODULE_TT_COPPER_F_EQUAL:
+	case MODULE_TT_COPPER_N_EQUAL:
+	case MODULE_TT_COPPER_LINEAR_EQUAL:
+	case MODULE_TT_COPPER_NF_LINEAR:
+	case MODULE_TT_COPPER_F_LINEAR:
+	case MODULE_TT_COPPER_N_LINEAR:
 		module_print_any_uint("Attenuation at 5GHz",
 				      map->page_00h[CMIS_COPPER_ATT_5GHZ], "db");
 		module_print_any_uint("Attenuation at 7GHz",
@@ -269,15 +279,20 @@ static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
 		module_print_any_uint("Attenuation at 25.8GHz",
 				      map->page_00h[CMIS_COPPER_ATT_25P8GHZ],
 				      "db");
-	} else if (map->page_01h) {
-		module_print_any_float("Laser wavelength",
-				       (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) |
-				        map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05),
-				       "nm");
-		module_print_any_float("Laser wavelength tolerance",
-				       (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) |
-				        map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005),
+		break;
+	default:
+		if (!map->page_01h)
+			break;
+		wl = ((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) |
+		      map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05;
+		wl_tol = ((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) |
+			  map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005;
+		if (!wl)
+			break;
+		module_print_any_float("Laser wavelength", wl, "nm");
+		module_print_any_float("Laser wavelength tolerance", wl_tol,
 				       "nm");
+		break;
 	}
 }
 
-- 
2.55.0


^ permalink raw reply related

* [PATCH] tty: ldisc: fix deadlock between ldisc_sem and rtnl_mutex
From: Yun Zhou @ 2026-07-16  6:47 UTC (permalink / raw)
  To: gregkh, jirislaby, socketcan
  Cc: linux-serial, mkl, linux-can, davem, edumazet, kuba, pabeni,
	horms, netdev, linux-kernel, yun.zhou

syzbot reported a circular lock dependency involving tty ldisc_sem and
the networking rtnl_mutex. The full chain is:

  rtnl_mutex --> nft_commit_mutex --> ... --> ep->mtx --> ldisc_sem --> rtnl_mutex

The last edge (ldisc_sem -> rtnl_mutex) is created because tty line
discipline .open() callbacks (slcan, slip) call register_netdev() which
acquires rtnl_mutex, and .open() runs under ldisc_sem write lock in
tty_set_ldisc().

Fix by moving the .open() call outside the ldisc_sem write lock. The
ldisc .open() is initialization of the NEW discipline after the old one
has been closed - there is no need for ldisc_sem protection at this
point since:

 - tty_lock is held throughout, preventing concurrent tty_set_ldisc,
   hangup, or close
 - tty->ldisc is set to NULL during the window, so concurrent readers
   (tty_ldisc_ref, tty_ldisc_ref_wait) see NULL and return immediately,
   which callers already handle as a hangup condition
 - tty buffer data stays queued until the ldisc is installed

The sequence becomes:
  1. Hold ldisc_sem(write): close old ldisc, set tty->ldisc = NULL
  2. Release ldisc_sem(write)
  3. Call new_ldisc->ops->open() without ldisc_sem
  4. Re-acquire ldisc_sem(write): install new ldisc (or restore old)
  5. Release ldisc_sem(write)

Reported-by: syzbot+de610eeef174bd59a8a3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=de610eeef174bd59a8a3
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
---
 drivers/tty/tty_ldisc.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 27fe8236f662..248a6995cc53 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -556,15 +556,28 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
 	/* Shutdown the old discipline. */
 	tty_ldisc_close(tty, old_ldisc);
 
-	/* Now set up the new line discipline. */
-	tty->ldisc = new_ldisc;
+	/* Clear tty->ldisc so concurrent readers back off during transition */
+	tty->ldisc = NULL;
 	tty_set_termios_ldisc(tty, disc);
+	tty_ldisc_unlock(tty);
 
+	/*
+	 * Open the new discipline outside ldisc_sem. The ldisc .open()
+	 * may acquire locks (e.g., rtnl_mutex) that would create circular
+	 * dependencies if taken under ldisc_sem. tty_lock is still held,
+	 * preventing concurrent ldisc changes and hangup.
+	 */
 	retval = tty_ldisc_open(tty, new_ldisc);
+
+	tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT);
+
 	if (retval < 0) {
 		/* Back to the old one or N_TTY if we can't */
 		tty_ldisc_put(new_ldisc);
 		tty_ldisc_restore(tty, old_ldisc);
+	} else {
+		/* Success - install new ldisc */
+		tty->ldisc = new_ldisc;
 	}
 
 	if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH ethtool 6/8] module-common: Add missing CMIS transmitter technologies
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

Add missing transmitter technologies from table 8-40 in CMIS 5.3. This
is a prerequisite for the next patch to help us distinguish between
copper and optical transceivers.

Fixes: 88ca347ef35a ("Add QSFP-DD support")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 module-common.c | 18 ++++++++++++++++++
 module-common.h |  5 +++++
 2 files changed, 23 insertions(+)

diff --git a/module-common.c b/module-common.c
index ea4514ab3899..f8e4681d8d63 100644
--- a/module-common.c
+++ b/module-common.c
@@ -628,6 +628,24 @@ void module_show_mit_compliance(u16 value)
 		snprintf(description, SFF_MAX_DESC_LEN, "%s linear active equalizers",
 			 cc);
 		break;
+	case MODULE_TT_C_BAND_LASER:
+		strncpy(description, "C-band tunable laser", SFF_MAX_DESC_LEN);
+		break;
+	case MODULE_TT_L_BAND_LASER:
+		strncpy(description, "L-band tunable laser", SFF_MAX_DESC_LEN);
+		break;
+	case MODULE_TT_COPPER_NF_LINEAR:
+		snprintf(description, SFF_MAX_DESC_LEN,
+			 "%s near and far end linear active equalizers", cc);
+		break;
+	case MODULE_TT_COPPER_F_LINEAR:
+		snprintf(description, SFF_MAX_DESC_LEN,
+			 "%s far end linear active equalizers", cc);
+		break;
+	case MODULE_TT_COPPER_N_LINEAR:
+		snprintf(description, SFF_MAX_DESC_LEN,
+			 "%s near end linear active equalizers", cc);
+		break;
 	default:
 		strncpy(description, "Reserved or unknown", SFF_MAX_DESC_LEN);
 		break;
diff --git a/module-common.h b/module-common.h
index fba1d36f7f86..e9bf8c19a9bb 100644
--- a/module-common.h
+++ b/module-common.h
@@ -99,6 +99,11 @@ enum module_type {
 #define MODULE_TT_COPPER_F_EQUAL		0x0D
 #define MODULE_TT_COPPER_N_EQUAL		0x0E
 #define MODULE_TT_COPPER_LINEAR_EQUAL		0x0F
+#define MODULE_TT_C_BAND_LASER			0x10
+#define MODULE_TT_L_BAND_LASER			0x11
+#define MODULE_TT_COPPER_NF_LINEAR		0x12
+#define MODULE_TT_COPPER_F_LINEAR		0x13
+#define MODULE_TT_COPPER_N_LINEAR		0x14
 
 /* Module Flags (Page 0) */
 #define CMIS_VCC_AW_OFFSET			0x09
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 5/8] module-common: Avoid undefined behavior with CMIS transceivers
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

While the function that handles the printing of the transmitter
technology handles all the values passed by the SFF-8636 parser (0-15),
it does not handle all the values that can be passed by the CMIS parser
where this field is 8-bits.

Fix by adding a default case that will initialize "description" to
"Reserved or unknown".

Fixes: 88ca347ef35a ("Add QSFP-DD support")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 module-common.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/module-common.c b/module-common.c
index 997dd2b37ce7..ea4514ab3899 100644
--- a/module-common.c
+++ b/module-common.c
@@ -628,6 +628,9 @@ void module_show_mit_compliance(u16 value)
 		snprintf(description, SFF_MAX_DESC_LEN, "%s linear active equalizers",
 			 cc);
 		break;
+	default:
+		strncpy(description, "Reserved or unknown", SFF_MAX_DESC_LEN);
+		break;
 	}
 
 	sff_print_any_hex_field("Transmitter technology",
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 4/8] module-common: Fix printing of transmitter technology for CMIS transceivers
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

In CMIS, the transceiver technology is encoded in the
"MediaInterfaceTechnology" field which is 8-bits, while in SFF-8636 it
is encoded in the 4 MSBs of the "Device Technology" field. Both
specifications share the same encoding for the common values (0-15), but
CMIS supports more values given the field is 8-bits.

Given the common encoding, cited commit consolidated the printing of
this field between CMIS and SFF-8636. The CMIS parser passes the
"MediaInterfaceTechnology" field and the SFF-8636 parser passes the
"Device Technology" field while masking the 4 LSBs. This creates a
situation where both parsers pass different values for the same
transmitter technology (e.g., "1310 nm VCSEL" is 0x01 in CMIS and 0x10
in SFF-8636). To overcome this, the common code matches on two values
for each transmitter technology.

While this works most of the time, it is going to create a problem for
CMIS transceivers that utilize the 4 MSBs of the
"MediaInterfaceTechnology" field.

Fix by creating a common encoding for the transmitter technology field
and have the SFF-8636 parser pass the shifted value of the "Device
Technology" field to the common code. That way both parsers pass the
same value for a given transmitter technology.

Fixes: 504c4f573571 ("module_common: Add a new file to all the common code for all module types")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c          |  4 ++--
 module-common.c | 47 +++++++++++++++-----------------------------
 module-common.h | 52 +++++++++++++++----------------------------------
 qsfp.c          |  5 ++---
 4 files changed, 36 insertions(+), 72 deletions(-)

diff --git a/cmis.c b/cmis.c
index da1ced75e4ef..19a3eb7cf925 100644
--- a/cmis.c
+++ b/cmis.c
@@ -254,11 +254,11 @@ static void cmis_show_sig_integrity(const struct cmis_memory_map *map)
  */
 static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
 {
-	u16 value = map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET];
+	__u8 value = map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET];
 
 	module_show_mit_compliance(value);
 
-	if (value >= CMIS_COPPER_UNEQUAL) {
+	if (value >= MODULE_TT_COPPER_UNEQUAL) {
 		module_print_any_uint("Attenuation at 5GHz",
 				      map->page_00h[CMIS_COPPER_ATT_5GHZ], "db");
 		module_print_any_uint("Attenuation at 7GHz",
diff --git a/module-common.c b/module-common.c
index 42fccf68301b..997dd2b37ce7 100644
--- a/module-common.c
+++ b/module-common.c
@@ -575,71 +575,56 @@ void module_show_mit_compliance(u16 value)
 	char description[SFF_MAX_DESC_LEN];
 
 	switch (value) {
-	case MODULE_850_VCSEL:
+	case MODULE_TT_850_VCSEL:
 		strncpy(description, "850 nm VCSEL", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1310_VCSEL:
-	case SFF8636_TRANS_1310_VCSEL:
+	case MODULE_TT_1310_VCSEL:
 		strncpy(description, "1310 nm VCSEL", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1550_VCSEL:
-	case SFF8636_TRANS_1550_VCSEL:
+	case MODULE_TT_1550_VCSEL:
 		strncpy(description, "1550 nm VCSEL", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1310_FP:
-	case SFF8636_TRANS_1310_FP:
+	case MODULE_TT_1310_FP:
 		strncpy(description, "1310 nm FP", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1310_DFB:
-	case SFF8636_TRANS_1310_DFB:
+	case MODULE_TT_1310_DFB:
 		strncpy(description, "1310 nm DFB", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1550_DFB:
-	case SFF8636_TRANS_1550_DFB:
+	case MODULE_TT_1550_DFB:
 		strncpy(description, "1550 nm DFB", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1310_EML:
-	case SFF8636_TRANS_1310_EML:
+	case MODULE_TT_1310_EML:
 		strncpy(description, "1310 nm EML", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1550_EML:
-	case SFF8636_TRANS_1550_EML:
+	case MODULE_TT_1550_EML:
 		strncpy(description, "1550 nm EML", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_OTHERS:
-	case SFF8636_TRANS_OTHERS:
+	case MODULE_TT_OTHERS:
 		strncpy(description, "Others/Undefined", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_1490_DFB:
-	case SFF8636_TRANS_1490_DFB:
+	case MODULE_TT_1490_DFB:
 		strncpy(description, "1490 nm DFB", SFF_MAX_DESC_LEN);
 		break;
-	case CMIS_COPPER_UNEQUAL:
-	case SFF8636_TRANS_COPPER_PAS_UNEQUAL:
+	case MODULE_TT_COPPER_UNEQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN, "%s unequalized", cc);
 		break;
-	case CMIS_COPPER_PASS_EQUAL:
-	case SFF8636_TRANS_COPPER_PAS_EQUAL:
+	case MODULE_TT_COPPER_PASS_EQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN, "%s passive equalized",
 			 cc);
 		break;
-	case CMIS_COPPER_NF_EQUAL:
-	case SFF8636_TRANS_COPPER_LNR_FAR_EQUAL:
+	case MODULE_TT_COPPER_NF_EQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN,
 			 "%s near and far end limiting active equalizers", cc);
 		break;
-	case CMIS_COPPER_F_EQUAL:
-	case SFF8636_TRANS_COPPER_FAR_EQUAL:
+	case MODULE_TT_COPPER_F_EQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN,
 			 "%s far end limiting active equalizers", cc);
 		break;
-	case CMIS_COPPER_N_EQUAL:
-	case SFF8636_TRANS_COPPER_NEAR_EQUAL:
+	case MODULE_TT_COPPER_N_EQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN,
 			 "%s near end limiting active equalizers", cc);
 		break;
-	case CMIS_COPPER_LINEAR_EQUAL:
-	case SFF8636_TRANS_COPPER_LNR_EQUAL:
+	case MODULE_TT_COPPER_LINEAR_EQUAL:
 		snprintf(description, SFF_MAX_DESC_LEN, "%s linear active equalizers",
 			 cc);
 		break;
diff --git a/module-common.h b/module-common.h
index 4063448f3eea..fba1d36f7f86 100644
--- a/module-common.h
+++ b/module-common.h
@@ -83,42 +83,22 @@ enum module_type {
 #define  MODULE_CTOR_VENDOR_LAST		0xFF
 
 /* Transmitter Technology */
-#define MODULE_850_VCSEL			0x00
-
-/* SFF8636 */
-#define	 SFF8636_TRANS_TECH_MASK		0xF0
-#define	 SFF8636_TRANS_COPPER_LNR_EQUAL		(15 << 4)
-#define	 SFF8636_TRANS_COPPER_NEAR_EQUAL	(14 << 4)
-#define	 SFF8636_TRANS_COPPER_FAR_EQUAL		(13 << 4)
-#define	 SFF8636_TRANS_COPPER_LNR_FAR_EQUAL	(12 << 4)
-#define	 SFF8636_TRANS_COPPER_PAS_EQUAL		(11 << 4)
-#define	 SFF8636_TRANS_COPPER_PAS_UNEQUAL	(10 << 4)
-#define	 SFF8636_TRANS_1490_DFB			(9 << 4)
-#define	 SFF8636_TRANS_OTHERS			(8 << 4)
-#define	 SFF8636_TRANS_1550_EML			(7 << 4)
-#define	 SFF8636_TRANS_1310_EML			(6 << 4)
-#define  SFF8636_TRANS_1550_DFB			(5 << 4)
-#define	 SFF8636_TRANS_1310_DFB			(4 << 4)
-#define	 SFF8636_TRANS_1310_FP			(3 << 4)
-#define	 SFF8636_TRANS_1550_VCSEL		(2 << 4)
-#define	 SFF8636_TRANS_1310_VCSEL		(1 << 4)
-
-/* CMIS */
-#define CMIS_1310_VCSEL				0x01
-#define CMIS_1550_VCSEL				0x02
-#define CMIS_1310_FP				0x03
-#define CMIS_1310_DFB				0x04
-#define CMIS_1550_DFB				0x05
-#define CMIS_1310_EML				0x06
-#define CMIS_1550_EML				0x07
-#define CMIS_OTHERS				0x08
-#define CMIS_1490_DFB				0x09
-#define CMIS_COPPER_UNEQUAL			0x0A
-#define CMIS_COPPER_PASS_EQUAL			0x0B
-#define CMIS_COPPER_NF_EQUAL			0x0C
-#define CMIS_COPPER_F_EQUAL			0x0D
-#define CMIS_COPPER_N_EQUAL			0x0E
-#define CMIS_COPPER_LINEAR_EQUAL		0x0F
+#define MODULE_TT_850_VCSEL			0x00
+#define MODULE_TT_1310_VCSEL			0x01
+#define MODULE_TT_1550_VCSEL			0x02
+#define MODULE_TT_1310_FP			0x03
+#define MODULE_TT_1310_DFB			0x04
+#define MODULE_TT_1550_DFB			0x05
+#define MODULE_TT_1310_EML			0x06
+#define MODULE_TT_1550_EML			0x07
+#define MODULE_TT_OTHERS			0x08
+#define MODULE_TT_1490_DFB			0x09
+#define MODULE_TT_COPPER_UNEQUAL		0x0A
+#define MODULE_TT_COPPER_PASS_EQUAL		0x0B
+#define MODULE_TT_COPPER_NF_EQUAL		0x0C
+#define MODULE_TT_COPPER_F_EQUAL		0x0D
+#define MODULE_TT_COPPER_N_EQUAL		0x0E
+#define MODULE_TT_COPPER_LINEAR_EQUAL		0x0F
 
 /* Module Flags (Page 0) */
 #define CMIS_VCC_AW_OFFSET			0x09
diff --git a/qsfp.c b/qsfp.c
index c82a3dec9b0b..20b6c2173284 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -562,12 +562,11 @@ static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map)
 static void
 sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map)
 {
-	u16 value = map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
-			SFF8636_TRANS_TECH_MASK;
+	__u8 value = map->page_00h[SFF8636_DEVICE_TECH_OFFSET] >> 4;
 
 	module_show_mit_compliance(value);
 
-	if (value >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
+	if (value >= MODULE_TT_COPPER_UNEQUAL) {
 		module_print_any_uint("Attenuation at 2.5GHz",
 				      map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET],
 				      "db");
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 3/8] cmis: Fix printing of CDB EPL pages
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

The "CdbMaxPagesEPL" field is 4-bits. The code currently assumes that
the value of the field directly determines the number of EPL pages, but
values 5 / 6 / 7 correspond to 8 / 12 / 16 EPL pages.

Fix the printing of the number of EPL pages by using a translation table
and avoid printing the field if the value is reserved (i.e., 8-15).

Fixes: fe8e296aa023 ("cmis: Print CDB messaging support advertisement")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/cmis.c b/cmis.c
index 16f168a3b1af..da1ced75e4ef 100644
--- a/cmis.c
+++ b/cmis.c
@@ -924,10 +924,14 @@ static void cmis_show_cdb_mode(const struct cmis_memory_map *map)
 
 static void cmis_show_cdb_epl_pages(const struct cmis_memory_map *map)
 {
-	__u8 epl_pages = map->page_01h[CMIS_CDB_ADVER_OFFSET] &
-			 CMIS_CDB_ADVER_EPL_MASK;
+	static const __u8 epl_page_count[] = { 0, 1, 2, 3, 4, 8, 12, 16 };
+	__u8 epl = map->page_01h[CMIS_CDB_ADVER_OFFSET] &
+		CMIS_CDB_ADVER_EPL_MASK;
 
-	module_print_any_uint("CDB EPL pages", epl_pages, NULL);
+	if (epl >= ARRAY_SIZE(epl_page_count))
+		return;
+
+	module_print_any_uint("CDB EPL pages", epl_page_count[epl], NULL);
 }
 
 static void cmis_show_cdb_rw_len(const struct cmis_memory_map *map)
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 2/8] cmis: Fix printing of channel-level flags
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

CMIS encodes different channel-level flags (e.g., "Laser bias current
high alarm") in different bytes where each bit corresponds to the value
of the flag in a different channel. Since CMIS supports more than 8
channels, these flags can be banked (e.g., channels 1-8 in bank 0,
channels 9-16 in bank 1 etc.).

The code is currently applying the wrong bit mask (the channel number
itself) when extracting the per-channel flag. Fix the calculation of the
bit mask so that the channel offset (0-7) in the current bank determines
the number of bits that are shifted.

Fixes: 340d88ee1289 ("cmis: Parse and print diagnostic information")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmis.c b/cmis.c
index a4bf455c2a77..16f168a3b1af 100644
--- a/cmis.c
+++ b/cmis.c
@@ -766,7 +766,7 @@ static void cmis_show_dom_chan_lvl_flag(const struct cmis_memory_map *map,
 		char str[80];
 		bool value;
 
-		value = page_11h[module_aw_chan_flags[flag].offset] & chan;
+		value = page_11h[module_aw_chan_flags[flag].offset] & (1 << i);
 		if (is_json_context()) {
 			print_bool(PRINT_JSON, NULL, NULL, value);
 		} else {
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 1/8] cmis: Fix printing of Tx bias current
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel
In-Reply-To: <20260716064532.1522382-1-idosch@nvidia.com>

In CMIS, unlike SFF-8636 and SFF-8472, the Tx bias current can be scaled
(i.e., multiplied by 1 / 2 / 4). Fix the application of the scaling
factor from shift right to shift left. In addition, increase the size of
the field that stores the scaled value from 16-bits to 32-bits to avoid
truncation.

Fixes: 340d88ee1289 ("cmis: Parse and print diagnostic information")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 cmis.c       | 10 +++++-----
 sff-common.h |  4 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/cmis.c b/cmis.c
index f494268ba3b7..a4bf455c2a77 100644
--- a/cmis.c
+++ b/cmis.c
@@ -517,7 +517,7 @@ cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map,
 
 		sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h,
 							   tx_bias_offset);
-		sd->scd[chan].bias_cur >>= bias_mul;
+		sd->scd[chan].bias_cur <<= bias_mul;
 		sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h,
 							   rx_power_offset);
 		sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h,
@@ -544,16 +544,16 @@ static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map,
 
 	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
 						CMIS_TX_BIAS_HALRM_OFFSET);
-	sd->bias_cur[HALRM] >>= bias_mul;
+	sd->bias_cur[HALRM] <<= bias_mul;
 	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
 						CMIS_TX_BIAS_LALRM_OFFSET);
-	sd->bias_cur[LALRM] >>= bias_mul;
+	sd->bias_cur[LALRM] <<= bias_mul;
 	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
 						CMIS_TX_BIAS_HWARN_OFFSET);
-	sd->bias_cur[HWARN] >>= bias_mul;
+	sd->bias_cur[HWARN] <<= bias_mul;
 	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
 						CMIS_TX_BIAS_LWARN_OFFSET);
-	sd->bias_cur[LWARN] >>= bias_mul;
+	sd->bias_cur[LWARN] <<= bias_mul;
 
 	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
 						CMIS_TX_PWR_HALRM_OFFSET);
diff --git a/sff-common.h b/sff-common.h
index 3c02a69ec7f6..59ae21038c9c 100644
--- a/sff-common.h
+++ b/sff-common.h
@@ -126,7 +126,7 @@
 
 /* Channel Monitoring Fields */
 struct sff_channel_diags {
-	__u16 bias_cur;      /* Measured bias current in 2uA units */
+	__u32 bias_cur;      /* Measured bias current in 2uA units */
 	__u16 rx_power;      /* Measured RX Power */
 	__u16 tx_power;      /* Measured TX Power */
 };
@@ -157,7 +157,7 @@ struct sff_diags {
 	/* SFP Temp in 16-bit signed 1/256 Celcius */
 	__s16 sfp_temp[5];
 	/* Measured bias current in 2uA units */
-	__u16 bias_cur[5];
+	__u32 bias_cur[5];
 	/* Measured TX Power */
 	__u16 tx_power[5];
 	/* Measured RX Power */
-- 
2.55.0


^ permalink raw reply related

* [PATCH ethtool 0/8] cmis: Miscellaneous fixes
From: Ido Schimmel @ 2026-07-16  6:45 UTC (permalink / raw)
  To: netdev; +Cc: mkubecek, danieller, Ido Schimmel

Patch #1 fixes an issue reported by a user. The rest fix valid issues
pointed out by Claude Code after I asked it to validate the CMIS parser
against the CMIS specification.

Note that these are not regressions (never worked).

Ido Schimmel (8):
  cmis: Fix printing of Tx bias current
  cmis: Fix printing of channel-level flags
  cmis: Fix printing of CDB EPL pages
  module-common: Fix printing of transmitter technology for CMIS
    transceivers
  module-common: Avoid undefined behavior with CMIS transceivers
  module-common: Add missing CMIS transmitter technologies
  cmis: Fix printing of attenuation and wavelength
  cmis: Fix printing of link length

 cmis.c          | 65 +++++++++++++++++++++++++++++++---------------
 cmis.h          |  1 +
 module-common.c | 68 +++++++++++++++++++++++++++----------------------
 module-common.h | 57 +++++++++++++++--------------------------
 qsfp.c          |  5 ++--
 sff-common.h    |  4 +--
 6 files changed, 108 insertions(+), 92 deletions(-)

-- 
2.55.0


^ permalink raw reply

* Re: [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Mieczyslaw Nalewaj @ 2026-07-16  6:40 UTC (permalink / raw)
  To: contact, Linus Walleij, Alvin Šipraga, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Russell King
  Cc: Maxime Chevallier, Luiz Angelo Daros de Luca, netdev,
	linux-kernel
In-Reply-To: <20260711-rtl8367s-sgmii-v6-1-88f7944ddca7@c127.dev>

Hi Johan

On 7/12/2026 6:31 AM, Johan Alvarado via B4 Relay wrote:
> From: Johan Alvarado <contact@c127.dev>
> 
> The RTL8367S can mux its embedded SerDes to external interface 1,
> which is typically used to connect the switch to a CPU port. The chip
> info table already declares SGMII as a supported interface mode for
> this chip, but the driver only implements RGMII so far.
> 
> Implement SGMII support as a phylink PCS, with the configuration
> sequence derived from the GPL-licensed Realtek rtl8367c vendor driver
> as distributed in the Mercusys MR80X GPL code drop:
> 
>  - Add accessors for the SerDes indirect access registers (SDS_INDACS),
>    through which the SerDes internal registers are reached.
> 
>  - Register a phylink_pcs for the SerDes, selected from mac_select_pcs
>    for the SGMII interface, so the SerDes handling lives in the PCS
>    operations rather than in the MAC operations.
> 
>  - Probe the SerDes tuning variant from the chip option register once
>    at setup. The vendor driver keeps two sets of SerDes tuning
>    parameters and selects between them based on this option; only the
>    variant for a non-zero option (which all RTL8367S parts seen so far
>    report) has been validated on hardware, so the SerDes interface
>    modes are only advertised in that case. An unsupported variant thus
>    fails at phylink validation time instead of at link configuration
>    time.
> 
>  - Keep the embedded DW8051 microcontroller in reset and disabled. The
>    vendor driver loads firmware into it to manage the SerDes link, but
>    analysis of that firmware shows it only duplicates the link
>    management phylink already performs: it polls the port status and
>    writes the external interface force registers behind the driver's
>    back.
> 
>  - Clear the line rate bypass bit for the external interface, tune the
>    SerDes with the vendor-prescribed parameters, mux the SerDes to MAC8
>    in SGMII mode and only then take the SerDes out of reset, as the
>    vendor driver does.
> 
>  - After deasserting the SerDes reset, reset the SerDes data path via
>    the SerDes BMCR register to flush the FIFOs and resync the PLL.
>    This mirrors what the vendor firmware does right after deasserting
>    the SerDes reset, and ensures a clean link state from cold boot.
> 
>  - Force the SGMII link parameters (link, speed, duplex) in the SDS_MISC
>    register from pcs_link_up(). SGMII in-band autonegotiation is not
>    implemented, so only fixed-link and conventional PHY setups are
>    supported, just like RGMII. This is reported to phylink through
>    pcs_inband_caps() returning LINK_INBAND_DISABLE, so phylink never
>    selects an in-band-enabled negotiation mode for this PCS.
> 
>  - Program the SerDes pause enables in SDS_MISC from the resolved
>    pause modes when forcing the MAC external interface in mac_link_up,
>    as the vendor driver does, rather than leaving whatever state the
>    boot firmware left there. Flow control testing shows these bits,
>    not the MAC force pause bits, gate pause on the SerDes external
>    interface. This is done in the MAC layer because pcs_link_up()
>    carries no pause information.
> 
>  - Implement pcs_get_state() by reading the link status from the
>    SerDes, with the forced speed and duplex read back from SDS_MISC.
>    Although the supported fixed-link and conventional PHY setups do not
>    use it, the PCS owns the SerDes link state, and phylink consults
>    pcs_get_state() to track the physical link when operating in in-band
>    mode with autonegotiation disabled. The SerDes has no link interrupt
>    wired up, so the PCS sets its poll flag.
> 
> Tested on a Mercusys MR80X v2.20, where the RTL8367S is connected to
> the SoC over SGMII.
> 
> Suggested-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Suggested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
> Signed-off-by: Johan Alvarado <contact@c127.dev>
> ---
>  drivers/net/dsa/realtek/rtl8365mb_main.c | 515 ++++++++++++++++++++++++++++++-
>  1 file changed, 511 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
> index 5ac091bf93c9..ea03c42d0f1a 100644
> --- a/drivers/net/dsa/realtek/rtl8365mb_main.c
> +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c

[...]

> +static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
> +				phy_interface_t interface,
> +				const unsigned long *advertising,
> +				bool permit_pause_to_mac)
> +{
> +	const int id = RTL8365MB_SDS_EXT_INTERFACE_ID;
> +	struct rtl8365mb *mb = pcs_to_rtl8365mb(pcs);
> +	struct realtek_priv *priv;
> +	u16 val;
> +	int ret;
> +	int i;
> +
> +	priv = mb->priv;
> +
> +	/* Hold the embedded DW8051 microcontroller in reset and keep it
> +	 * disabled. The vendor driver loads firmware into it to manage the
> +	 * SerDes link, but the firmware only duplicates work that phylink
> +	 * already does: it polls the port status and forces the external
> +	 * interface configuration in the very registers this driver manages.
> +	 * Letting it run would race with phylink.
> +	 */
> +	ret = regmap_update_bits(priv->map, RTL8365MB_CHIP_RESET_REG,
> +				 RTL8365MB_CHIP_RESET_DW8051_MASK,
> +				 RTL8365MB_CHIP_RESET_DW8051_MASK);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0_REG,
> +				 RTL8365MB_MISC_CFG0_DW8051_EN_MASK, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* The vendor driver clears the line rate bypass for all interface
> +	 * modes except TMII.
> +	 */
> +	ret = regmap_update_bits(priv->map, RTL8365MB_BYPASS_LINE_RATE_REG,
> +				 RTL8365MB_SDS_BYPASS_LINE_RATE_MASK, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* Tune the SerDes with vendor-prescribed parameters */
> +	for (i = 0; i < ARRAY_SIZE(rtl8365mb_sds_jam_sgmii); i++) {
> +		ret = rtl8365mb_sds_write(priv,
> +					  rtl8365mb_sds_jam_sgmii[i].reg,
> +					  rtl8365mb_sds_jam_sgmii[i].val);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Mux the SerDes to MAC8 in SGMII mode */
> +	ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
> +				 RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK |
> +					 RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK,
> +				 RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK);
> +	if (ret)
> +		return ret;
> +
> +	val = RTL8365MB_EXT_PORT_MODE_SGMII
> +	      << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(id);
> +	ret = regmap_update_bits(priv->map,
> +				 RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(id),
> +				 RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(id),
> +				 val);
> +	if (ret)
> +		return ret;
> +
> +	/* Take the SerDes out of reset. The vendor driver does this only
> +	 * after the SerDes mux and the interface mode are configured.
> +	 */
> +	ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_RESET,
> +				  RTL8365MB_SDS_RESET_DEASSERT);
> +	if (ret)
> +		return ret;
> +
> +	/* Reset the SerDes data path and resync its PLL, mirroring what the
> +	 * vendor firmware does right after deasserting the SerDes reset.
> +	 * This flushes the FIFOs and ensures a clean state for the link,
> +	 * preventing silent drops and CRC errors.
> +	 */
> +	ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
> +				  RTL8365MB_SDS_BMCR_DPRST_PHASE1);
> +	if (ret)
> +		return ret;
> +
> +	ret = rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_BMCR,
> +				  RTL8365MB_SDS_BMCR_DPRST_PHASE2);
> +	if (ret)
> +		return ret;
> +
> +	/* Keep SGMII in-band autonegotiation disabled: the link parameters are
> +	 * forced from rtl8365mb_pcs_link_up() instead.
> +	 */
> +	ret = rtl8365mb_sds_read(priv, RTL8365MB_SDS_REG_NWAY, &val);
> +	if (ret)
> +		return ret;
> +
> +	val &= ~RTL8365MB_SDS_NWAY_EN_MASK;
> +	val |= RTL8365MB_SDS_NWAY_RESTART_MASK;
> +
> +	return rtl8365mb_sds_write(priv, RTL8365MB_SDS_REG_NWAY, val);
> +}

For verification on real hardware:

The SGMII/HSGMII link on RTL8367S (and compatible RTL8365MB-VC) can be
intermittently unstable after cold boot, manifesting as egress stalls,
CRC errors or complete packet loss on the SerDes-attached CPU port.
This has been observed as a non-deterministic failure depending on the
PLL lock state at power-on.

Reverse-engineering of the vendor DW8051 firmware blob (Sgmii_Init[])
shows that the vendor driver performs two critical steps after the
SerDes data-path reset (BMCR DPRST_PHASE2) which were missing from the
Linux driver:

1. A ~98 ms delay to let the SerDes PLL fully lock before any further
   register access. The vendor firmware uses a timer interrupt to count
   this delay; without it the analog front-end may still be settling.

2. Writing a "Local Jam Table" calibration vector to internal ASIC
   registers 0x060C-0x060F (values 0x83, 0xAA, 0x7E, 0x80). These
   registers configure the SerDes analog equalizer and DC-offset and
   are not exposed through the normal SDS_INDACS window. Omitting them
   leaves the analog front-end in an uncalibrated state.

Add both steps to rtl8365mb_pcs_config(), immediately after the BMCR
phase-2 data-path reset and before the NWAY configuration. This mirrors
the exact vendor firmware bring-up sequence and eliminates the cold-boot
race.

The 98 ms delay matches the vendor firmware timeout constant
(SGMII_TIMEOUT_98MS). It is a one-time cost during interface bring-up
and ensures reliable link establishment regardless of the PLL state at
reset.

--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -95,6 +95,7 @@
 #include <linux/if_bridge.h>
 #include <linux/if_vlan.h>
 #include <linux/phylink.h>
+#include <linux/delay.h>
 
 #include "realtek.h"
 #include "realtek-smi.h"
@@ -1235,6 +1236,21 @@ static int rtl8365mb_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
 	if (ret)
 		return ret;
 
+	/* The vendor firmware waits ~98 ms here before writing the
+	 * calibration vector.
+	 */
+	usleep_range(98000, 98500);
+
+	/* Vendor firmware writes a "Local Jam Table" to internal ASIC
+	 * registers 0x060C-0x060F after the data path reset to calibrate
+	 * the SerDes analog front-end. Without this, the link can be
+	 * unstable after cold boot.
+	 */
+	regmap_write(priv->map, 0x060C, 0x0083);
+	regmap_write(priv->map, 0x060D, 0x00AA);
+	regmap_write(priv->map, 0x060E, 0x007E);
+	regmap_write(priv->map, 0x060F, 0x0080);
+
 	/* Keep SGMII in-band autonegotiation disabled: the link parameters are
 	 * forced from rtl8365mb_pcs_link_up() instead.
 	 */


^ permalink raw reply

* [PATCH net v2] mctp: check register_netdevice_notifier() error in mctp_device_init()
From: Minhong He @ 2026-07-16  6:35 UTC (permalink / raw)
  To: jk; +Cc: heminhong, matt, netdev, davem, edumazet, kuba, pabeni, horms,
	kuniyu
In-Reply-To: <20d74693ac673ba1db0c0052a7236db777fc5ec5.camel@codeconstruct.com.au>

mctp_device_init() handles errors from rtnl_af_register() and
rtnl_register_many(), but ignores the return value of
register_netdevice_notifier(). If notifier registration fails, init can
still return success while the module is only partially initialized.

Check the notifier registration error and fail module init early.

Fixes: d51705614f66 ("mctp: Handle error of rtnl_register_module().")
Signed-off-by: Minhong He <heminhong@kylinos.cn>
---
 net/mctp/device.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/mctp/device.c b/net/mctp/device.c
index 2c84df674669..822120e860c8 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -536,7 +536,9 @@ int __init mctp_device_init(void)
 {
 	int err;
 
-	register_netdevice_notifier(&mctp_dev_nb);
+	err = register_netdevice_notifier(&mctp_dev_nb);
+	if (err)
+		return err;
 
 	err = rtnl_af_register(&mctp_af_ops);
 	if (err)
-- 
2.25.1


^ permalink raw reply related

* [PATCH net v5 3/3] octeon_ep_vf: fix skb frags overflow in the RX path
From: Maoyi Xie @ 2026-07-16  6:34 UTC (permalink / raw)
  To: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
	Shinas Rasheed
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maciej Fijalkowski, Guangshuo Li, David Carlier,
	Simon Horman, netdev, linux-kernel
In-Reply-To: <20260716063432.2908100-1-maoyixie.tju@gmail.com>

__octep_vf_oq_process_rx() has the same unbounded fragment loop as the PF
driver. buff_info->len comes from the device response header. The loop adds
one fragment per buffer_size chunk with no check against MAX_SKB_FRAGS. A
long packet yields about 18 fragments. That is one past the default
MAX_SKB_FRAGS of 17. skb_add_rx_frag() then writes past shinfo->frags[].

The fragment count is now checked before napi_build_skb(). A packet that
needs more fragments than the skb can hold is dropped.

octep_vf_oq_drop_rx() drains those descriptors. It also frees the head page
and every fragment page. The previous patch added those frees to the inline
drop path. The napi_build_skb() failure path now uses the same helper.

Fixes: 1cd3b407977c ("octeon_ep_vf: add Tx/Rx processing and interrupt support")
Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
---
 .../marvell/octeon_ep_vf/octep_vf_rx.c        | 52 ++++++++++++-------
 1 file changed, 33 insertions(+), 19 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
index 302559b16b..138e779f7e 100644
--- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
@@ -357,6 +357,31 @@ static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
 	return (idx + 1 == oq->max_count) ? 0 : idx + 1;
 }
 
+static void octep_vf_oq_drop_rx(struct octep_vf_oq *oq,
+				struct octep_vf_rx_buffer *buff_info,
+				void *resp_hw, u32 *read_idx, u32 *desc_used)
+{
+	u32 data_len = buff_info->len - oq->max_single_buffer_size;
+
+	put_page(virt_to_page(resp_hw));
+	(*desc_used)++;
+	*read_idx = octep_vf_oq_next_idx(oq, *read_idx);
+	while (data_len) {
+		dma_unmap_page(oq->dev, oq->desc_ring[*read_idx].buffer_ptr,
+			       PAGE_SIZE, DMA_FROM_DEVICE);
+		buff_info = (struct octep_vf_rx_buffer *)
+			    &oq->buff_info[*read_idx];
+		put_page(buff_info->page);
+		buff_info->page = NULL;
+		if (data_len < oq->buffer_size)
+			data_len = 0;
+		else
+			data_len -= oq->buffer_size;
+		(*desc_used)++;
+		*read_idx = octep_vf_oq_next_idx(oq, *read_idx);
+	}
+}
+
 /**
  * __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack.
  *
@@ -430,29 +455,18 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
 			read_idx = octep_vf_oq_next_idx(oq, read_idx);
 		} else {
 			struct skb_shared_info *shinfo;
-			u16 data_len;
+			u32 data_len;
+
+			data_len = buff_info->len - oq->max_single_buffer_size;
+			if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
+				octep_vf_oq_drop_rx(oq, buff_info, resp_hw, &read_idx, &desc_used);
+				continue;
+			}
 
 			skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
 			if (!skb) {
 				oq->stats->alloc_failures++;
-				put_page(virt_to_page(resp_hw));
-				desc_used++;
-				read_idx = octep_vf_oq_next_idx(oq, read_idx);
-				data_len = buff_info->len - oq->max_single_buffer_size;
-				while (data_len) {
-					dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
-						       PAGE_SIZE, DMA_FROM_DEVICE);
-					buff_info = (struct octep_vf_rx_buffer *)
-						    &oq->buff_info[read_idx];
-					put_page(buff_info->page);
-					buff_info->page = NULL;
-					if (data_len < oq->buffer_size)
-						data_len = 0;
-					else
-						data_len -= oq->buffer_size;
-					desc_used++;
-					read_idx = octep_vf_oq_next_idx(oq, read_idx);
-				}
+				octep_vf_oq_drop_rx(oq, buff_info, resp_hw, &read_idx, &desc_used);
 				continue;
 			}
 			rx_bytes += buff_info->len;
-- 
2.34.1


^ permalink raw reply related

* [PATCH net v5 2/3] octeon_ep_vf: Fix RX page leak on napi_build_skb() failure
From: Maoyi Xie @ 2026-07-16  6:34 UTC (permalink / raw)
  To: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
	Shinas Rasheed
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maciej Fijalkowski, Guangshuo Li, David Carlier,
	Simon Horman, netdev, linux-kernel
In-Reply-To: <20260716063432.2908100-1-maoyixie.tju@gmail.com>

From: Guangshuo Li <lgs201920130244@gmail.com>

__octep_vf_oq_process_rx() clears buff_info->page before building an skb
from the RX page. On the success path the page is consumed by the skb,
either as the skb head or as an RX fragment.

If napi_build_skb() fails, however, the page is not consumed by an skb.
The error path advances the descriptor and leaves the ring slot cleared,
so the page is no longer tracked and is leaked. In the multi-fragment
case, the remaining fragment pages are also unmapped and removed from
their ring slots without being released.

Release the head page when napi_build_skb() fails, and release each
remaining fragment page before clearing its ring slot.

Fixes: dd66b4285470 ("octeon_ep_vf: add NULL check for napi_build_skb()")
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
 drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
index d982474082..302559b16b 100644
--- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
@@ -418,6 +418,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
 			skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
 			if (!skb) {
 				oq->stats->alloc_failures++;
+				put_page(virt_to_page(resp_hw));
 				desc_used++;
 				read_idx = octep_vf_oq_next_idx(oq, read_idx);
 				continue;
@@ -434,6 +435,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
 			skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
 			if (!skb) {
 				oq->stats->alloc_failures++;
+				put_page(virt_to_page(resp_hw));
 				desc_used++;
 				read_idx = octep_vf_oq_next_idx(oq, read_idx);
 				data_len = buff_info->len - oq->max_single_buffer_size;
@@ -442,6 +444,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
 						       PAGE_SIZE, DMA_FROM_DEVICE);
 					buff_info = (struct octep_vf_rx_buffer *)
 						    &oq->buff_info[read_idx];
+					put_page(buff_info->page);
 					buff_info->page = NULL;
 					if (data_len < oq->buffer_size)
 						data_len = 0;
-- 
2.34.1


^ permalink raw reply related

* [PATCH net v5 1/3] octeon_ep: fix skb frags overflow in the RX path
From: Maoyi Xie @ 2026-07-16  6:34 UTC (permalink / raw)
  To: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
	Shinas Rasheed
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maciej Fijalkowski, Guangshuo Li, David Carlier,
	Simon Horman, netdev, linux-kernel
In-Reply-To: <20260716063432.2908100-1-maoyixie.tju@gmail.com>

__octep_oq_process_rx() builds an skb for a multi-buffer packet by adding
one fragment per buffer_size chunk:

	data_len = buff_info->len - oq->max_single_buffer_size;
	while (data_len) {
		...
		skb_add_rx_frag(skb, shinfo->nr_frags, buff_info->page, 0,
				buff_info->len, buff_info->len);
		...
	}

buff_info->len comes from the device response header
(be64_to_cpu(resp_hw->length)). Nothing bounds the fragment count against
MAX_SKB_FRAGS. data_len can be close to 65535. buffer_size defaults to
about 3776 on 4K pages, so a full packet yields about 18 fragments. That
is one more than the default MAX_SKB_FRAGS of 17, so skb_add_rx_frag()
writes past shinfo->frags[].

The fragment count is now checked before build_skb(). A packet that needs
more fragments than the skb can hold is dropped. octep_oq_drop_rx()
consumes its descriptors like the build_skb failure path. The same class
was fixed in other RX paths, including commit 5ffcb7b890f6 ("net: atlantic:
fix fragment overflow handling in RX path") and commit f0813bcd2d9d ("net:
wwan: t7xx: fix potential skb->frags overflow in RX path").

Fixes: 37d79d059606 ("octeon_ep: add Tx/Rx processing and interrupt support")
Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
---
 drivers/net/ethernet/marvell/octeon_ep/octep_rx.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
index e6ebc7e44a..1e5494c652 100644
--- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
@@ -453,6 +453,15 @@ static int __octep_oq_process_rx(struct octep_device *oct,
 
 		octep_oq_next_pkt(oq, buff_info, &read_idx, &desc_used);
 
+		if (buff_info->len > oq->max_single_buffer_size) {
+			u32 data_len = buff_info->len - oq->max_single_buffer_size;
+
+			if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
+				octep_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
+				continue;
+			}
+		}
+
 		skb = build_skb((void *)resp_hw, PAGE_SIZE);
 		if (!skb) {
 			octep_oq_drop_rx(oq, buff_info,
-- 
2.34.1


^ permalink raw reply related

* [PATCH net v5 0/3] octeon_ep, octeon_ep_vf: fix RX skb frags overflow and page leak
From: Maoyi Xie @ 2026-07-16  6:34 UTC (permalink / raw)
  To: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
	Shinas Rasheed
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Maciej Fijalkowski, Guangshuo Li, David Carlier,
	Simon Horman, netdev, linux-kernel

The octeon_ep and octeon_ep_vf RX paths add one skb fragment per buffer
with no bound against MAX_SKB_FRAGS. buff_info->len comes from the device
response header. A long packet needs about 18 fragments. That is one past
the default MAX_SKB_FRAGS of 17, so skb_add_rx_frag() writes past
shinfo->frags[]. Patch 1 bounds octeon_ep. Patch 3 bounds octeon_ep_vf.

Patch 2 is Guangshuo Li's fix for an octeon_ep_vf RX page leak on the
napi_build_skb() failure path. It touches the same drop code as patch 3.
I carry it here so the series applies without conflict, per Maciej. Patch 3
moves that drain loop into a helper. The helper carries the page frees from
patch 2, so the overflow drop path frees its pages too.

octeon_ep has the same leak on its drop path. A separate patch will fix it
once this series lands.

v5:
 - octeon_ep, octeon_ep_vf: widen data_len to u32, per Simon Horman.
   buff_info->len is a u64, a u16 could truncate a long length.
 - octeon_ep: dropped Maciej Fijalkowski's Reviewed-by, the check changed.
 - octeon_ep_vf: the drop helper drains the length in a u32 too.

v1: https://lore.kernel.org/r/20260701112825.1653044-1-maoyixie.tju@gmail.com
v2: https://lore.kernel.org/r/20260702180518.2013324-1-maoyixie.tju@gmail.com
v3: https://lore.kernel.org/r/20260704061511.2350737-1-maoyixie.tju@gmail.com
v4: https://lore.kernel.org/r/20260706150208.2944898-1-maoyixie.tju@gmail.com

Guangshuo Li (1):
  octeon_ep_vf: Fix RX page leak on napi_build_skb() failure

Maoyi Xie (2):
  octeon_ep: fix skb frags overflow in the RX path
  octeon_ep_vf: fix skb frags overflow in the RX path

 .../net/ethernet/marvell/octeon_ep/octep_rx.c |  9 ++++
 .../marvell/octeon_ep_vf/octep_vf_rx.c        | 51 ++++++++++++-------
 2 files changed, 43 insertions(+), 17 deletions(-)

--
2.34.1


^ permalink raw reply

* Re: [PATCH v4 1/3] drm/drm_ras: Add drm_ras netlink error event
From: Tauro, Riana @ 2026-07-16  6:07 UTC (permalink / raw)
  To: Raag Jadav
  Cc: Rodrigo Vivi, kuba, aravind.iddamsetty, intel-xe, anshuman.gupta,
	joonas.lahtinen, simona.vetter, airlied, pratik.bari,
	joshua.santosh.ranjan, ashwin.kumar.kulkarni, shubham.kumar,
	ravi.kishore.koppuravuri, maarten.lankhorst, mallesh.koujalagi,
	soham.purkait, Zack McKevitt, Lijo Lazar, Hawking Zhang,
	David S. Miller, Paolo Abeni, Eric Dumazet, dri-devel, netdev
In-Reply-To: <ak9oUmZ8sS3aF6jB@black.igk.intel.com>


On 09-07-2026 14:52, Raag Jadav wrote:
> On Thu, Jul 09, 2026 at 10:45:27AM +0530, Tauro, Riana wrote:
>> On 09-07-2026 01:51, Rodrigo Vivi wrote:
>>> On Tue, Jul 07, 2026 at 12:02:11PM +0530, Tauro, Riana wrote:
>>>> Hi Rodrigo/Jakub/Aravind
>>>>
>>>> Please let me know if you have any feedback for this patch or can you please
>>>> ack this if it looks good to you.
>>> I looks good to me, but could you please double check the sashiko's comments?
>> Sashiko has a comment regarding namespaces
>>
>> [Severity: Medium]Since the generic netlink family explicitly supports
>> multiple networknamespaces by setting .netnsok = true,
>>   will hardcoding init_net hereprevent listeners in non-init namespaces from
>> receiving error events
>>
>> .netnsok = true is auto generated not explicitly added in code.
> Because that's what ynl_gen_c.py does without it being parsed from
> anywhere.
>
>> But from what i see, drm device is present in the host and not replicated
>> across namespaces
>> and most of the non-network implementations use inet.  (ex: Binder)
>> If this really needs a fix, we can drop the has_listeners suggested by raag
>> in previous patch.
>> I don't think in an error path, allocating a new buffer would be a big
>> overhead if listeners are not present.
> I don't know enough about namespaces to comment on this. I'll rely on
> Jakub's disposition if netnsok is relevant here, or needed at all for
> our usecase.


I tried to find out more about network namespaces.  Network namespaces 
are created everytime docker is run without --network host.
With this patch, event notifications are not received in docker env 
though other commands work correctly.

Since this is a valid usecase for drm, removing has_listeners as it is a 
small overhead in error path and replacing genlmsg_multicast with
genlmsg_multicast_allns.

Thanks
Riana


>
> Raag

^ permalink raw reply

* Re: [PATCH v2] virtio_net: fix infinite loop in virtnet_poll_cleantx when device is broken
From: Jinqian Yang @ 2026-07-16  6:05 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: jasowang, xuanzhuo, eperezma, andrew+netdev, davem, edumazet,
	kuba, pabeni, netdev, virtualization, linux-kernel, liuyonglong,
	wangzhou1, linuxarm
In-Reply-To: <20260716011312-mutt-send-email-mst@kernel.org>

Hi,

On 2026/7/16 13:14, Michael S. Tsirkin wrote:
> On Thu, Jul 16, 2026 at 11:52:01AM +0800, Jinqian Yang wrote:
>> virtnet_poll_cleantx() contains a do-while loop that cleans up
>> transmitted TX buffers and calls virtqueue_enable_cb_delayed() to check
>> whether more buffers need processing. When the virtio backend stops
>> responding during guest reboot, used->idx is never updated, so
>> virtqueue_enable_cb_delayed() always returns false and the loop never
>> terminates. Then it will block reboot process, and the guest will hang.
>>
>> The problem occurs during guest reboot under network traffic:
>>
>>    1. kernel_restart() -> device_shutdown() traverses the device list
>>    2. virtio_dev_shutdown() calls virtio_break_device() which sets
>>       vq->broken = true
>>    3. virtio_dev_shutdown() then calls virtio_synchronize_cbs() to wait
>>       for in-flight callbacks to complete
>>    4. A virtio interrupt fires, softirq is deferred to ksoftirqd which
>>       calls net_rx_action() -> virtnet_poll() -> virtnet_poll_cleantx()
>>    5. virtnet_poll_cleantx() enters the do-while loop and never exits
>>       because the QEMU backend has stopped updating used->idx, despite
>>       vq->broken having been set to true in step 2.
>>
>> Since the loop runs inside ksoftirqd (a SCHED_OTHER kthread), it is
>> visible to the scheduler and does not trigger a hard lockup. However,
>> the kthread never leaves the loop, so RCU detects it as a CPU stall
>> and reports it periodically. Meanwhile, the reboot process remains
>> blocked in device_shutdown() because virtio_dev_shutdown() cannot
>> complete its synchronization step, and the guest hangs permanently.
>>
>> This can be reproduced on a guest with a virtio-net device: run iperf3
>> traffic in the guest, then trigger reboot. The reboot occasionally hangs
>> permanently with RCU stall on ksoftirqd.
>>
>> Observed on ARM64 KVM guest:
>>
>>    CPU#1 RCU stall (ksoftirqd/1), repeated periodically:
>>      virtqueue_enable_cb_delayed_split <- virtnet_poll <- __napi_poll <-
>>      net_rx_action <- handle_softirqs <- run_ksoftirqd <-
>>      smpboot_thread_fn <- kthread
>>
>> Fix by adding a virtqueue_is_broken() check to the loop condition, so
>> that the loop exits immediately when the device is broken, allowing
>> the device shutdown to proceed.
>>
>> Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com>
> 
> 
> Good thanks! Just the subject needs change so it's clear
> we are changing virtio core not virtio net.
> 

Thanks for the catch. Will change the subject prefix to "virtio_ring:"
and send v3 shortly.

Thanks,
Jinqian

>> ---
>> Changes in v2:
>>    - Moved vq->broken check to virtqueue_enable_cb_delayed().
>>
>> v1: https://lore.kernel.org/lkml/20260713132025.703147-1-yangjinqian1@huawei.com/
>> ---
>>   drivers/virtio/virtio_ring.c | 8 ++++++++
>>   1 file changed, 8 insertions(+)
>>
>> diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
>> index b438dc2ce1b8..5c169fbb418a 100644
>> --- a/drivers/virtio/virtio_ring.c
>> +++ b/drivers/virtio/virtio_ring.c
>> @@ -3233,6 +3233,14 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
>>   {
>>   	struct vring_virtqueue *vq = to_vvq(_vq);
>>   
>> +	/*
>> +	 * When the device is broken there is no point in polling used->idx,
>> +	 * the backend will never update it. Return true to let callers
>> +	 * exit their cleanup loops instead of spinning forever.
>> +	 */
>> +	if (unlikely(vq->broken))
>> +		return true;
>> +
>>   	if (vq->event_triggered)
>>   		data_race(vq->event_triggered = false);
>>   
>> -- 
>> 2.33.0
> 
> 


^ permalink raw reply

* Re: [PATCH net-next v3] net: skb: isolate skb data area allocations into a separate bucket
From: Kees Cook @ 2026-07-16  5:54 UTC (permalink / raw)
  To: Pedro Falcato, Harry Yoo
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Jason Xing, Kuniyuki Iwashima, netdev, linux-kernel,
	linux-hardening, linux-mm, Vlastimil Babka
In-Reply-To: <aldpAY6nXwgayHL0@pedro-suse.lan>



On July 15, 2026 4:07:49 AM PDT, Pedro Falcato <pfalcato@suse.de> wrote:
>On Wed, Jul 08, 2026 at 10:27:54PM +0900, Harry Yoo wrote:
>> 
>> 
>> On 7/8/26 8:16 PM, Pedro Falcato wrote:
>> > On Wed, Jul 08, 2026 at 10:30:50AM +0200, Paolo Abeni wrote:
>> >> On 7/2/26 7:07 PM, Pedro Falcato wrote:> @@ -586,6 +586,8 @@ struct
>> >> sk_buff *napi_build_skb(void *data, unsigned int frag_size)
>> >>>  }
>> >>>  EXPORT_SYMBOL(napi_build_skb);
>> >>>  
>> >>> +static kmem_buckets *skb_data_buckets __ro_after_init;
>> >>> +
>> >>>  static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
>> >>>  {
>> >>>  	if (!gfp_pfmemalloc_allowed(flags))
>> >>> @@ -593,7 +595,8 @@ static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
>> >>>  	if (!obj_size)
>> >>>  		return kmem_cache_alloc_node(net_hotdata.skb_small_head_cache,
>> >>>  					     flags, node);
>> >>> -	return kmalloc_node_track_caller(obj_size, flags, node);
>> >>> +	return kmem_buckets_alloc_node_track_caller(skb_data_buckets, obj_size,
>> >>> +						    flags, node);
>> >>
>> >> Sashiko noted that some drivers may require GFP_DMA buckets, and the
>> >> above may break them:
>> >>
>> >> https://sashiko.dev/#/patchset/20260702170728.168755-1-pfalcato%40suse.de
>> > 
>> > Oh, this is really awkward. Adding linux-mm and slab maintainers for input here.
>> > 
>> > Considering the current slab bucketing does not seem to duplicate DMA or
>> > CGROUP caches, could it make sense to duplicate those as well?
>> 
>> Could we specify what kmalloc types the user needs when creating
>> kmem_buckets and duplicate caches for the requested kmalloc types only?
>
>Perhaps. But do the users themselves know? alloc_skb() allows users to specify
>random __GFP flags. We're bound to see some random caller do
>alloc_skb(__GFP_ACCOUNT) ;)
>
>In all honesty, I'm not quite sure what the best way forward here is. The most
>transparent way is to bucket those other kmalloc types as well, but that might
>very trivially result in a lot more caches (and possibly memory usage) for no
>great reason. So perhaps specifying caches might do.

How about dropping non-standard flag alloc requests into the general kmalloc buckets? (I.e. have kmem_buckets_alloc redirect on flag mismatch?)

-Kees

-- 
Kees Cook

^ permalink raw reply

* Re: [PATCH v2] virtio_net: fix infinite loop in virtnet_poll_cleantx when device is broken
From: Michael S. Tsirkin @ 2026-07-16  5:14 UTC (permalink / raw)
  To: Jinqian Yang
  Cc: jasowang, xuanzhuo, eperezma, andrew+netdev, davem, edumazet,
	kuba, pabeni, netdev, virtualization, linux-kernel, liuyonglong,
	wangzhou1, linuxarm
In-Reply-To: <20260716035201.3736582-1-yangjinqian1@huawei.com>

On Thu, Jul 16, 2026 at 11:52:01AM +0800, Jinqian Yang wrote:
> virtnet_poll_cleantx() contains a do-while loop that cleans up
> transmitted TX buffers and calls virtqueue_enable_cb_delayed() to check
> whether more buffers need processing. When the virtio backend stops
> responding during guest reboot, used->idx is never updated, so
> virtqueue_enable_cb_delayed() always returns false and the loop never
> terminates. Then it will block reboot process, and the guest will hang.
> 
> The problem occurs during guest reboot under network traffic:
> 
>   1. kernel_restart() -> device_shutdown() traverses the device list
>   2. virtio_dev_shutdown() calls virtio_break_device() which sets
>      vq->broken = true
>   3. virtio_dev_shutdown() then calls virtio_synchronize_cbs() to wait
>      for in-flight callbacks to complete
>   4. A virtio interrupt fires, softirq is deferred to ksoftirqd which
>      calls net_rx_action() -> virtnet_poll() -> virtnet_poll_cleantx()
>   5. virtnet_poll_cleantx() enters the do-while loop and never exits
>      because the QEMU backend has stopped updating used->idx, despite
>      vq->broken having been set to true in step 2.
> 
> Since the loop runs inside ksoftirqd (a SCHED_OTHER kthread), it is
> visible to the scheduler and does not trigger a hard lockup. However,
> the kthread never leaves the loop, so RCU detects it as a CPU stall
> and reports it periodically. Meanwhile, the reboot process remains
> blocked in device_shutdown() because virtio_dev_shutdown() cannot
> complete its synchronization step, and the guest hangs permanently.
> 
> This can be reproduced on a guest with a virtio-net device: run iperf3
> traffic in the guest, then trigger reboot. The reboot occasionally hangs
> permanently with RCU stall on ksoftirqd.
> 
> Observed on ARM64 KVM guest:
> 
>   CPU#1 RCU stall (ksoftirqd/1), repeated periodically:
>     virtqueue_enable_cb_delayed_split <- virtnet_poll <- __napi_poll <-
>     net_rx_action <- handle_softirqs <- run_ksoftirqd <-
>     smpboot_thread_fn <- kthread
> 
> Fix by adding a virtqueue_is_broken() check to the loop condition, so
> that the loop exits immediately when the device is broken, allowing
> the device shutdown to proceed.
> 
> Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com>


Good thanks! Just the subject needs change so it's clear
we are changing virtio core not virtio net.

> ---
> Changes in v2:
>   - Moved vq->broken check to virtqueue_enable_cb_delayed().
> 
> v1: https://lore.kernel.org/lkml/20260713132025.703147-1-yangjinqian1@huawei.com/
> ---
>  drivers/virtio/virtio_ring.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
> index b438dc2ce1b8..5c169fbb418a 100644
> --- a/drivers/virtio/virtio_ring.c
> +++ b/drivers/virtio/virtio_ring.c
> @@ -3233,6 +3233,14 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
>  {
>  	struct vring_virtqueue *vq = to_vvq(_vq);
>  
> +	/*
> +	 * When the device is broken there is no point in polling used->idx,
> +	 * the backend will never update it. Return true to let callers
> +	 * exit their cleanup loops instead of spinning forever.
> +	 */
> +	if (unlikely(vq->broken))
> +		return true;
> +
>  	if (vq->event_triggered)
>  		data_race(vq->event_triggered = false);
>  
> -- 
> 2.33.0


^ permalink raw reply

* Re: [RFC PATCH net-next v2 1/2] tcp: Add net.ipv4.tcp_purge_receive_queue sysctl
From: Leon Hwang @ 2026-07-16  4:47 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, David S . Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Jonathan Corbet, Shuah Khan, Neal Cardwell,
	Kuniyuki Iwashima, Ido Schimmel, Ilpo Järvinen,
	Chia-Yu Chang, Yung Chih Su, Wyatt Feng, Jason Xing, Lance Yang,
	Jiayuan Chen, linux-doc, linux-kernel, linux-kselftest
In-Reply-To: <CANn89iLyeAPNV2VHTM3w0-zpzjZNFy3aJDsVUERYyniLN4ub7A@mail.gmail.com>

On 15/7/26 23:15, Eric Dumazet wrote:
> On Wed, Jul 15, 2026 at 4:54 PM Leon Hwang <leon.hwang@linux.dev> wrote:

[...]

> 
> My thoughts are:
> 
> out_of_order_queue has been forgotten. skbs could be there and still
> 'block devmem'


Yes. The tp->out_of_order_queue should also be purged here so that
out-of-order SKBs cannot continue holding devmem.

> 
> WRITE_ONCE(tp->copied_seq, tp->rcv_nxt) is certainly wrong, because
> read() will return 0, instead of -1 (errno = EPIPE or ECONNRESET)
> So the application will not know a RST was received :/


Thanks for pointing this out. When sysctl_tcp_purge_receive_queue is
enabled, read() must report the reset error.

The reason read() returns 0 is that SOCK_DONE, which was set when the
FIN was processed, is checked before sk_err in tcp_recvmsg_locked(). I
think clearing SOCK_DONE after purging the queues could let read()
observes the error installed by tcp_done_with_error().

Will also update the packetdrill test to expect -1/EPIPE.

> 
> I think that BSD and linux implementations have historically retained
> acknowledged,
> buffered receive data upon RST to allow applications to drain data
> already ACKed prior to the reset.


Agreed. The new sysctl is disabled by default specifically to preserve
this existing behavior.

Enabling it is an explicit opt-in to discard buffered receive data,
release the associated resources promptly, and report the reset error
immediately.

Will update the change in ip-sysctl.rst documentation with this
application-visible tradeoff.

> 
> Adding a narrow sysctl specifically for CLOSE_WAIT creates
> inconsistent behavior across TCP states.


My bad. After reading the RFC 9293 section 3.10.7.4. [1] again, "All
segment queues should be flushed." should apply to these states:

      -  ESTABLISHED STATE
      -  FIN-WAIT-1 STATE
      -  FIN-WAIT-2 STATE
      -  CLOSE-WAIT STATE

So, the sysctl-controlled purge should be consistently applied to these
four states.

[1] https://www.rfc-editor.org/rfc/rfc9293.html#section-3.10.7.4

Thanks,
Leon


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox