Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/pci Add MSI/MSI-X support written by Kengo Nakahara....



details:   https://anonhg.NetBSD.org/src/rev/834d30e92b83
branches:  trunk
changeset: 338873:834d30e92b83
user:      msaitoh <msaitoh%NetBSD.org@localhost>
date:      Sat Jun 13 15:47:58 2015 +0000

description:
Add MSI/MSI-X support written by Kengo Nakahara. Some old devices' support
is written by me. It's disabled by default. If you'd like to use, define
WM_MSI_MSIX.

Tested with:
        8254[3405617] (INTx even if it has MSI CAP because of a errata)
        8257[12], 82583 ICH8, ICH10, PCH2, PCH_LPT(I21[78]) (MSI)
        8257[456], 82580, I35[04], I21[01] (MSI-X)

Not tested:
        82542, 82573, 80003, ICH9, PCH,

diffstat:

 sys/dev/pci/if_wm.c    |  579 +++++++++++++++++++++++++++++++++++++++++++++---
 sys/dev/pci/if_wmreg.h |   46 +++-
 2 files changed, 588 insertions(+), 37 deletions(-)

diffs (truncated from 933 to 300 lines):

diff -r 5c9e619e44f4 -r 834d30e92b83 sys/dev/pci/if_wm.c
--- a/sys/dev/pci/if_wm.c       Sat Jun 13 14:56:45 2015 +0000
+++ b/sys/dev/pci/if_wm.c       Sat Jun 13 15:47:58 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_wm.c,v 1.334 2015/06/12 04:40:28 msaitoh Exp $      */
+/*     $NetBSD: if_wm.c,v 1.335 2015/06/13 15:47:58 msaitoh Exp $      */
 
 /*
  * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
@@ -81,7 +81,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.334 2015/06/12 04:40:28 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.335 2015/06/13 15:47:58 msaitoh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -154,6 +154,31 @@
 #define WM_MPSAFE      1
 #endif
 
+#ifdef __HAVE_PCI_MSI_MSIX
+#if 0 /* off by default */
+#define WM_MSI_MSIX    1
+#endif
+#endif
+
+/*
+ * This device driver divides interrupt to TX, RX and link state.
+ * Each MSI-X vector indexes are below.
+ */
+#define WM_NINTR               3
+#define WM_TX_INTR_INDEX       0
+#define WM_RX_INTR_INDEX       1
+#define WM_LINK_INTR_INDEX     2
+#define WM_MAX_NINTR           WM_NINTR
+
+/*
+ * This device driver set affinity to each interrupts like below (round-robin).
+ * If the number CPUs is less than the number of interrupts, this driver usase
+ * the same CPU for multiple interrupts.
+ */
+#define WM_TX_INTR_CPUID       0
+#define WM_RX_INTR_CPUID       1
+#define WM_LINK_INTR_CPUID     2
+
 /*
  * Transmit descriptor list size.  Due to errata, we can only have
  * 256 hardware descriptors in the ring on < 82544, but we use 4096
@@ -295,7 +320,13 @@
        int sc_flowflags;               /* 802.3x flow control flags */
        int sc_align_tweak;
 
-       void *sc_ih;                    /* interrupt cookie */
+       void *sc_ihs[WM_MAX_NINTR];     /*
+                                        * interrupt cookie.
+                                        * legacy and msi use sc_ihs[0].
+                                        */
+       pci_intr_handle_t *sc_intrs;    /* legacy and msi use sc_intrs[0] */
+       int sc_nintrs;                  /* number of interrupts */
+
        callout_t sc_tick_ch;           /* tick callout */
        bool sc_stopping;
 
@@ -593,13 +624,18 @@
 static void    wm_nq_start(struct ifnet *);
 static void    wm_nq_start_locked(struct ifnet *);
 /* Interrupt */
-static void    wm_txintr(struct wm_softc *);
-static void    wm_rxintr(struct wm_softc *);
+static int     wm_txeof(struct wm_softc *);
+static void    wm_rxeof(struct wm_softc *);
 static void    wm_linkintr_gmii(struct wm_softc *, uint32_t);
 static void    wm_linkintr_tbi(struct wm_softc *, uint32_t);
 static void    wm_linkintr_serdes(struct wm_softc *, uint32_t);
 static void    wm_linkintr(struct wm_softc *, uint32_t);
-static int     wm_intr(void *);
+static int     wm_intr_legacy(void *);
+#ifdef WM_MSI_MSIX
+static int     wm_txintr_msix(void *);
+static int     wm_rxintr_msix(void *);
+static int     wm_linkintr_msix(void *);
+#endif
 
 /*
  * Media related.
@@ -1368,7 +1404,11 @@
        prop_dictionary_t dict;
        struct ifnet *ifp = &sc->sc_ethercom.ec_if;
        pci_chipset_tag_t pc = pa->pa_pc;
+#ifndef WM_MSI_MSIX
        pci_intr_handle_t ih;
+#else
+       bool intr_established = false;
+#endif
        const char *intrstr = NULL;
        const char *eetype, *xname;
        bus_space_tag_t memt;
@@ -1424,6 +1464,19 @@
                        sc->sc_type = WM_T_82542_2_0;
        }
 
+       /*
+        * Disable MSI for Errata:
+        * "Message Signaled Interrupt Feature May Corrupt Write Transactions"
+        * 
+        *  82544: Errata 25
+        *  82540: Errata  6 (easy to reproduce device timeout)
+        *  82545: Errata  4 (easy to reproduce device timeout)
+        *  82546: Errata 26 (easy to reproduce device timeout)
+        *  82541: Errata  7 (easy to reproduce device timeout)
+        */
+       if (sc->sc_type <= WM_T_82541_2)
+               pa->pa_flags &= ~PCI_FLAGS_MSI_OKAY;
+
        if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576)
            || (sc->sc_type == WM_T_82580)
            || (sc->sc_type == WM_T_I350) || (sc->sc_type == WM_T_I354)
@@ -1517,6 +1570,7 @@
                return;
        }
 
+#ifndef WM_MSI_MSIX
        /*
         * Map and establish our interrupt.
         */
@@ -1528,8 +1582,8 @@
 #ifdef WM_MPSAFE
        pci_intr_setattr(pc, &ih, PCI_INTR_MPSAFE, true);
 #endif
-       sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, wm_intr, sc);
-       if (sc->sc_ih == NULL) {
+       sc->sc_ihs[0] = pci_intr_establish(pc, ih, IPL_NET, wm_intr_legacy,sc);
+       if (sc->sc_ihs[0] == NULL) {
                aprint_error_dev(sc->sc_dev, "unable to establish interrupt");
                if (intrstr != NULL)
                        aprint_error(" at %s", intrstr);
@@ -1537,6 +1591,171 @@
                return;
        }
        aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
+       sc->sc_nintrs = 1;
+#else /* WM_MSI_MSIX */
+       if (pci_msix_alloc_exact(pa, &sc->sc_intrs, WM_NINTR) == 0) {
+               /* 1st, try to use MSI-X */
+               void *vih;
+               kcpuset_t *affinity;
+
+               kcpuset_create(&affinity, false);
+
+               /*
+                * for TX
+                */
+               intrstr = pci_intr_string(pc, sc->sc_intrs[WM_TX_INTR_INDEX],
+                   intrbuf, sizeof(intrbuf));
+#ifdef WM_MPSAFE
+               pci_intr_setattr(pc, &sc->sc_intrs[WM_TX_INTR_INDEX],
+                   PCI_INTR_MPSAFE, true);
+#endif
+               vih = pci_intr_establish(pc, sc->sc_intrs[WM_TX_INTR_INDEX],
+                   IPL_NET, wm_txintr_msix, sc);
+               if (vih == NULL) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to establish MSI-X(for TX)%s%s\n",
+                           intrstr ? " at " : "", intrstr ? intrstr : "");
+                       pci_intr_release(sc->sc_pc, sc->sc_intrs,
+                           WM_NINTR);
+                       goto msi;
+               }
+               kcpuset_zero(affinity);
+               /* Round-robin affinity */
+               kcpuset_set(affinity, WM_TX_INTR_CPUID % ncpu);
+               error = pci_intr_distribute(vih, affinity, NULL);
+               if (error == 0) {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for TX interrupting at %s affinity to %u\n",
+                           intrstr, WM_TX_INTR_CPUID % ncpu);
+               } else {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for TX interrupting at %s\n",
+                           intrstr);
+               }
+               sc->sc_ihs[WM_TX_INTR_INDEX] = vih;
+
+               /*
+                * for RX
+                */
+               intrstr = pci_intr_string(pc, sc->sc_intrs[WM_RX_INTR_INDEX],
+                   intrbuf, sizeof(intrbuf));
+#ifdef WM_MPSAFE
+               pci_intr_setattr(pc, &sc->sc_intrs[WM_RX_INTR_INDEX],
+                   PCI_INTR_MPSAFE, true);
+#endif
+               vih = pci_intr_establish(pc, sc->sc_intrs[WM_RX_INTR_INDEX],
+                   IPL_NET, wm_rxintr_msix, sc);
+               if (vih == NULL) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to establish MSI-X(for RX)%s%s\n",
+                           intrstr ? " at " : "", intrstr ? intrstr : "");
+                       pci_intr_release(sc->sc_pc, sc->sc_intrs,
+                           WM_NINTR);
+                       goto msi;
+               }
+               kcpuset_zero(affinity);
+               kcpuset_set(affinity, WM_RX_INTR_CPUID % ncpu);
+               error = pci_intr_distribute(vih, affinity, NULL);
+               if (error == 0) {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for RX interrupting at %s affinity to %u\n",
+                           intrstr, WM_RX_INTR_CPUID % ncpu);
+               } else {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for RX interrupting at %s\n",
+                           intrstr);
+               }
+               sc->sc_ihs[WM_RX_INTR_INDEX] = vih;
+
+               /*
+                * for link state changing
+                */
+               intrstr = pci_intr_string(pc, sc->sc_intrs[WM_LINK_INTR_INDEX],
+                   intrbuf, sizeof(intrbuf));
+#ifdef WM_MPSAFE
+               pci_intr_setattr(pc, &sc->sc_intrs[WM_LINK_INTR_INDEX],
+                   PCI_INTR_MPSAFE, true);
+#endif
+               vih = pci_intr_establish(pc, sc->sc_intrs[WM_LINK_INTR_INDEX],
+                   IPL_NET, wm_linkintr_msix, sc);
+               if (vih == NULL) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to establish MSI-X(for LINK)%s%s\n",
+                           intrstr ? " at " : "", intrstr ? intrstr : "");
+                       pci_intr_release(sc->sc_pc, sc->sc_intrs,
+                           WM_NINTR);
+                       goto msi;
+               }
+               kcpuset_zero(affinity);
+               kcpuset_set(affinity, WM_LINK_INTR_CPUID % ncpu);
+               error = pci_intr_distribute(vih, affinity, NULL);
+               if (error == 0) {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for LINK interrupting at %s affinity to %u\n",
+                           intrstr, WM_LINK_INTR_CPUID % ncpu);
+               } else {
+                       aprint_normal_dev(sc->sc_dev,
+                           "for LINK interrupting at %s\n",
+                           intrstr);
+               }
+               sc->sc_ihs[WM_LINK_INTR_INDEX] = vih;
+
+               sc->sc_nintrs = WM_NINTR;
+               kcpuset_destroy(affinity);
+               intr_established = true;
+       }
+
+msi:
+       if ((intr_established == false)
+           && (pci_msi_alloc_exact(pa, &sc->sc_intrs, 1) == 0)) {
+               /* 2nd, try to use MSI */
+               intrstr = pci_intr_string(pc, sc->sc_intrs[0], intrbuf,
+                   sizeof(intrbuf));
+#ifdef WM_MPSAFE
+               pci_intr_setattr(pc, &sc->sc_intrs[0], PCI_INTR_MPSAFE, true);
+#endif
+               sc->sc_ihs[0] = pci_intr_establish(pc, sc->sc_intrs[0],
+                   IPL_NET, wm_intr_legacy, sc);
+               if (sc->sc_ihs[0] == NULL) {
+                       aprint_error_dev(sc->sc_dev, "unable to establish MSI\n");
+                       pci_intr_release(sc->sc_pc, sc->sc_intrs,
+                           1);
+                       goto intx;
+               }
+               aprint_normal_dev(sc->sc_dev, "MSI at %s\n", intrstr);
+
+               sc->sc_nintrs = 1;
+               intr_established = true;
+       }
+
+intx:
+       if ((intr_established == false)
+           && (pci_intx_alloc(pa, &sc->sc_intrs) == 0)) {
+               /* Last, try to use INTx */
+               intrstr = pci_intr_string(pc, sc->sc_intrs[0], intrbuf,
+                   sizeof(intrbuf));
+#ifdef WM_MPSAFE
+               pci_intr_setattr(pc, &sc->sc_intrs[0], PCI_INTR_MPSAFE, true);
+#endif
+               sc->sc_ihs[0] = pci_intr_establish(pc, sc->sc_intrs[0],
+                   IPL_NET, wm_intr_legacy, sc);
+               if (sc->sc_ihs[0] == NULL) {
+                       aprint_error_dev(sc->sc_dev, "unable to establish MSI\n");
+                       pci_intr_release(sc->sc_pc, sc->sc_intrs, 1);
+                       goto int_failed;
+               }
+               aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
+
+               sc->sc_nintrs = 1;
+               intr_established = true;
+       }



Home | Main Index | Thread Index | Old Index