Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Workaround for ihphy and atphy(ICH*/PCH*, 82580 ...



details:   https://anonhg.NetBSD.org/src/rev/000863f38bb3
branches:  trunk
changeset: 941963:000863f38bb3
user:      knakahara <knakahara%NetBSD.org@localhost>
date:      Mon Nov 02 09:21:50 2020 +0000

description:
Workaround for ihphy and atphy(ICH*/PCH*, 82580 and I350).

These phys stop DMA while link is down which causes device timeout.
Fix PR/kern 40981

Reviewed and tested by msaitoh@n.o, thanks.

XXX pullup-[89]

diffstat:

 sys/dev/pci/if_wm.c |  119 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 116 insertions(+), 3 deletions(-)

diffs (208 lines):

diff -r 0467d8b5bb07 -r 000863f38bb3 sys/dev/pci/if_wm.c
--- a/sys/dev/pci/if_wm.c       Mon Nov 02 08:37:59 2020 +0000
+++ b/sys/dev/pci/if_wm.c       Mon Nov 02 09:21:50 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_wm.c,v 1.694 2020/10/30 06:29:47 msaitoh Exp $      */
+/*     $NetBSD: if_wm.c,v 1.695 2020/11/02 09:21:50 knakahara Exp $    */
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -82,7 +82,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.694 2020/10/30 06:29:47 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.695 2020/11/02 09:21:50 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -384,7 +384,8 @@
         * to manage Tx H/W queue's busy flag.
         */
        int txq_flags;                  /* flags for H/W queue, see below */
-#define        WM_TXQ_NO_SPACE 0x1
+#define        WM_TXQ_NO_SPACE         0x1
+#define        WM_TXQ_LINKDOWN_DISCARD 0x2
 
        bool txq_stopping;
 
@@ -1044,6 +1045,9 @@
 static int     wm_platform_pm_pch_lpt(struct wm_softc *, bool);
 static int     wm_pll_workaround_i210(struct wm_softc *);
 static void    wm_legacy_irq_quirk_spt(struct wm_softc *);
+static bool    wm_phy_need_linkdown_discard(struct wm_softc *);
+static void    wm_set_linkdown_discard(struct wm_softc *);
+static void    wm_clear_linkdown_discard(struct wm_softc *);
 
 #ifdef WM_DEBUG
 static int     wm_sysctl_debug(SYSCTLFN_PROTO);
@@ -3100,6 +3104,9 @@
 
        sc->sc_txrx_use_workqueue = false;
 
+       if (wm_phy_need_linkdown_discard(sc))
+               wm_set_linkdown_discard(sc);
+
        wm_init_sysctls(sc);
 
        if (pmf_device_register(self, wm_suspend, wm_resume))
@@ -3483,6 +3490,49 @@
        return rc;
 }
 
+static bool
+wm_phy_need_linkdown_discard(struct wm_softc *sc)
+{
+
+       switch(sc->sc_phytype) {
+       case WMPHY_82577: /* ihphy */
+       case WMPHY_82578: /* atphy */
+       case WMPHY_82579: /* ihphy */
+       case WMPHY_I217: /* ihphy */
+       case WMPHY_82580: /* ihphy */
+       case WMPHY_I350: /* ihphy */
+               return true;
+       default:
+               return false;
+       }
+}
+
+static void
+wm_set_linkdown_discard(struct wm_softc *sc)
+{
+
+       for (int i = 0; i < sc->sc_nqueues; i++) {
+               struct wm_txqueue *txq = &sc->sc_queue[i].wmq_txq;
+
+               mutex_enter(txq->txq_lock);
+               txq->txq_flags |= WM_TXQ_LINKDOWN_DISCARD;
+               mutex_exit(txq->txq_lock);
+       }
+}
+
+static void
+wm_clear_linkdown_discard(struct wm_softc *sc)
+{
+
+       for (int i = 0; i < sc->sc_nqueues; i++) {
+               struct wm_txqueue *txq = &sc->sc_queue[i].wmq_txq;
+
+               mutex_enter(txq->txq_lock);
+               txq->txq_flags &= ~WM_TXQ_LINKDOWN_DISCARD;
+               mutex_exit(txq->txq_lock);
+       }
+}
+
 /*
  * wm_ioctl:           [ifnet interface function]
  *
@@ -3520,6 +3570,12 @@
                }
                WM_CORE_UNLOCK(sc);
                error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
+               if (error == 0 && wm_phy_need_linkdown_discard(sc)) {
+                       if (IFM_SUBTYPE(ifr->ifr_media) == IFM_NONE)
+                               wm_set_linkdown_discard(sc);
+                       else
+                               wm_clear_linkdown_discard(sc);
+               }
                break;
        case SIOCINITIFADDR:
                WM_CORE_LOCK(sc);
@@ -3534,8 +3590,17 @@
                        break;
                }
                WM_CORE_UNLOCK(sc);
+               if (((ifp->if_flags & IFF_UP) == 0) && wm_phy_need_linkdown_discard(sc))
+                       wm_clear_linkdown_discard(sc);
                /*FALLTHROUGH*/
        default:
+               if (cmd == SIOCSIFFLAGS && wm_phy_need_linkdown_discard(sc)) {
+                       if (((ifp->if_flags & IFF_UP) == 0) && ((ifr->ifr_flags & IFF_UP) != 0)) {
+                               wm_clear_linkdown_discard(sc);
+                       } else if (((ifp->if_flags & IFF_UP) != 0) && ((ifr->ifr_flags & IFF_UP) == 0)) {
+                               wm_set_linkdown_discard(sc);
+                       }
+               }
 #ifdef WM_MPSAFE
                s = splnet();
 #endif
@@ -7674,6 +7739,16 @@
        return ((cpuid + ncpu - sc->sc_affinity_offset) % ncpu) % sc->sc_nqueues;
 }
 
+static inline bool
+wm_linkdown_discard(struct wm_txqueue *txq)
+{
+
+       if ((txq->txq_flags & WM_TXQ_LINKDOWN_DISCARD) != 0)
+               return true;
+
+       return false;
+}
+
 /*
  * wm_start:           [ifnet interface function]
  *
@@ -7767,6 +7842,23 @@
        if ((txq->txq_flags & WM_TXQ_NO_SPACE) != 0)
                return;
 
+       if (__predict_false(wm_linkdown_discard(txq))) {
+               do {
+                       if (is_transmit)
+                               m0 = pcq_get(txq->txq_interq);
+                       else
+                               IFQ_DEQUEUE(&ifp->if_snd, m0);
+                       /*
+                        * increment successed packet counter as in the case
+                        * which the packet is discarded by link down PHY.
+                        */
+                       if (m0 != NULL)
+                               if_statinc(ifp, if_opackets);
+                       m_freem(m0);
+               } while (m0 != NULL);
+               return;
+       }
+
        /* Remember the previous number of free descriptors. */
        ofree = txq->txq_free;
 
@@ -8367,6 +8459,23 @@
        if ((txq->txq_flags & WM_TXQ_NO_SPACE) != 0)
                return;
 
+       if (__predict_false(wm_linkdown_discard(txq))) {
+               do {
+                       if (is_transmit)
+                               m0 = pcq_get(txq->txq_interq);
+                       else
+                               IFQ_DEQUEUE(&ifp->if_snd, m0);
+                       /*
+                        * increment successed packet counter as in the case
+                        * which the packet is discarded by link down PHY.
+                        */
+                       if (m0 != NULL)
+                               if_statinc(ifp, if_opackets);
+                       m_freem(m0);
+               } while (m0 != NULL);
+               return;
+       }
+
        sent = false;
 
        /*
@@ -9234,9 +9343,13 @@
                DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> up %s\n",
                        device_xname(dev),
                        (status & STATUS_FD) ? "FDX" : "HDX"));
+               if (wm_phy_need_linkdown_discard(sc))
+                       wm_clear_linkdown_discard(sc);
        } else {
                DPRINTF(sc, WM_DEBUG_LINK, ("%s: LINK: LSC -> down\n",
                        device_xname(dev)));
+               if (wm_phy_need_linkdown_discard(sc))
+                       wm_set_linkdown_discard(sc);
        }
        if ((sc->sc_type == WM_T_ICH8) && (link == false))
                wm_gig_downshift_workaround_ich8lan(sc);



Home | Main Index | Thread Index | Old Index