git: 766cceba19fe - stable/14 - e6000sw: Fix locking in miibus_{read,write}reg implementations

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 13 Nov 2023 15:20:21 UTC
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=766cceba19fe05095d81994e930a489ad545d42b

commit 766cceba19fe05095d81994e930a489ad545d42b
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-11-06 19:57:56 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-11-13 14:41:50 +0000

    e6000sw: Fix locking in miibus_{read,write}reg implementations
    
    Commit 469290648005e13b819a19353032ca53dda4378f made e6000sw's
    implementation of miibus_(read|write)reg assume that the softc lock is
    held.  I presume that is to avoid lock recursion in e6000sw_attach() ->
    e6000sw_attach_miibus() -> mii_attach() -> MIIBUS_READREG().
    
    However, the lock assertion in e6000sw_readphy_locked() can fail if a
    different driver uses the interface to probe registers.  Work around the
    problem by providing implementations which lock the softc if it is not
    already locked.
    
    PR:             274795
    Fixes:          469290648005 ("e6000sw: add readphy and writephy wrappers")
    Reviewed by:    kp, imp
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D42466
    
    (cherry picked from commit 725962a9f4c050b21488edd58d317e87c76d6f66)
---
 sys/dev/etherswitch/e6000sw/e6000sw.c | 31 +++++++++++++++++++------------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c
index 768d1b927e3e..95f1a2e96db6 100644
--- a/sys/dev/etherswitch/e6000sw/e6000sw.c
+++ b/sys/dev/etherswitch/e6000sw/e6000sw.c
@@ -66,6 +66,7 @@ MALLOC_DEFINE(M_E6000SW, "e6000sw", "e6000sw switch");
 #define	E6000SW_UNLOCK(_sc)		sx_unlock(&(_sc)->sx)
 #define	E6000SW_LOCK_ASSERT(_sc, _what)	sx_assert(&(_sc)->sx, (_what))
 #define	E6000SW_TRYLOCK(_sc)		sx_tryxlock(&(_sc)->sx)
+#define	E6000SW_LOCKED(_sc)		sx_xlocked(&(_sc)->sx)
 #define	E6000SW_WAITREADY(_sc, _reg, _bit)				\
     e6000sw_waitready((_sc), REG_GLOBAL, (_reg), (_bit))
 #define	E6000SW_WAITREADY2(_sc, _reg, _bit)				\
@@ -169,8 +170,8 @@ static device_method_t e6000sw_methods[] = {
 	DEVMETHOD(bus_add_child,		device_add_child_ordered),
 
 	/* mii interface */
-	DEVMETHOD(miibus_readreg,		e6000sw_readphy_locked),
-	DEVMETHOD(miibus_writereg,		e6000sw_writephy_locked),
+	DEVMETHOD(miibus_readreg,		e6000sw_readphy),
+	DEVMETHOD(miibus_writereg,		e6000sw_writephy),
 
 	/* etherswitch interface */
 	DEVMETHOD(etherswitch_getinfo,		e6000sw_getinfo),
@@ -744,17 +745,20 @@ e6000sw_write_xmdio(device_t dev, int phy, int devaddr, int devreg, int val)
 	return (0);
 }
 
-static int e6000sw_readphy(device_t dev, int phy, int reg)
+static int
+e6000sw_readphy(device_t dev, int phy, int reg)
 {
 	e6000sw_softc_t *sc;
-	int ret;
+	int locked, ret;
 
 	sc = device_get_softc(dev);
-	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
 
-	E6000SW_LOCK(sc);
+	locked = E6000SW_LOCKED(sc);
+	if (!locked)
+		E6000SW_LOCK(sc);
 	ret = e6000sw_readphy_locked(dev, phy, reg);
-	E6000SW_UNLOCK(sc);
+	if (!locked)
+		E6000SW_UNLOCK(sc);
 
 	return (ret);
 }
@@ -795,17 +799,20 @@ e6000sw_readphy_locked(device_t dev, int phy, int reg)
 	return (val & PHY_DATA_MASK);
 }
 
-static int e6000sw_writephy(device_t dev, int phy, int reg, int data)
+static int
+e6000sw_writephy(device_t dev, int phy, int reg, int data)
 {
 	e6000sw_softc_t *sc;
-	int ret;
+	int locked, ret;
 
 	sc = device_get_softc(dev);
-	E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
 
-	E6000SW_LOCK(sc);
+	locked = E6000SW_LOCKED(sc);
+	if (!locked)
+		E6000SW_LOCK(sc);
 	ret = e6000sw_writephy_locked(dev, phy, reg, data);
-	E6000SW_UNLOCK(sc);
+	if (!locked)
+		E6000SW_UNLOCK(sc);
 
 	return (ret);
 }