This makes ixl(4) use MSI-X where available.  The hardware is set up
for the same kind of approach as we're heading towards in em(4) and
ix(4) - interrupts for admin commands and events (link state etc.)
can only be delivered to vector 0, and the natural approach is to
map rx and tx queues to other vectors, so that's what I've done here.

The driver was already set up for multiple rx/tx queues (though it only
uses one still), so the diff sets up one vector per queue.  The vector
setup here involves creating linked lists of interrupt causes, which
are identified by a queue type (tx or rx) and a queue index.  The
queues also need to be told which msix vector they interrupt on.
This is done through per-vector and per-queue registers.

I've tested this with and without msix on amd64 with this nic:
ixl0 at pci14 dev 0 function 0 "Intel X710 SFP+" rev 0x02: port 3, FW 6.0.48754 
API 1.7, msix, 1 queue

ok?

Index: if_ixl.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_ixl.c,v
retrieving revision 1.47
diff -u -p -r1.47 if_ixl.c
--- if_ixl.c    22 Apr 2020 07:09:40 -0000      1.47
+++ if_ixl.c    28 Apr 2020 00:24:02 -0000
@@ -1092,6 +1092,13 @@ struct ixl_atq {
 };
 SIMPLEQ_HEAD(ixl_atq_list, ixl_atq);
 
+struct ixl_queue_intr {
+       struct ixl_softc        *sc;
+       int                      queue;
+       void                    *ihc;
+       char                     name[8];
+};
+
 struct ixl_softc {
        struct device            sc_dev;
        struct arpcom            sc_ac;
@@ -1103,6 +1110,7 @@ struct ixl_softc {
        pci_intr_handle_t        sc_ih;
        void                    *sc_ihc;
        pcitag_t                 sc_tag;
+       struct ixl_queue_intr   *sc_qintr;
 
        bus_dma_tag_t            sc_dmat;
        bus_space_tag_t          sc_memt;
@@ -1160,6 +1168,8 @@ struct ixl_softc {
 static void    ixl_clear_hw(struct ixl_softc *);
 static int     ixl_pf_reset(struct ixl_softc *);
 
+static int     ixl_setup_msix(struct ixl_softc *, struct pci_attach_args *);
+
 static int     ixl_dmamem_alloc(struct ixl_softc *, struct ixl_dmamem *,
                    bus_size_t, u_int);
 static void    ixl_dmamem_free(struct ixl_softc *, struct ixl_dmamem *);
@@ -1214,7 +1224,8 @@ static void       ixl_media_status(struct ifne
 static void    ixl_watchdog(struct ifnet *);
 static int     ixl_ioctl(struct ifnet *, u_long, caddr_t);
 static void    ixl_start(struct ifqueue *);
-static int     ixl_intr(void *);
+static int     ixl_intr0(void *);
+static int     ixl_intr_queue(void *);
 static int     ixl_up(struct ixl_softc *);
 static int     ixl_down(struct ixl_softc *);
 static int     ixl_iff(struct ixl_softc *);
@@ -1524,13 +1535,24 @@ ixl_attach(struct device *parent, struct
                goto shutdown;
        }
 
-       if (pci_intr_map_msi(pa, &sc->sc_ih) != 0 &&
-           pci_intr_map(pa, &sc->sc_ih) != 0) {
-               printf(", unable to map interrupt\n");
-               goto shutdown;
+       if (pci_intr_map_msix(pa, 0, &sc->sc_ih) == 0) {
+               sc->sc_qintr = mallocarray(sizeof(struct ixl_queue_intr),
+                   ixl_nqueues(sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
+               if (sc->sc_qintr == NULL) {
+                       printf(", unable to allocate queue interrupts\n");
+                       goto shutdown;
+               }
+       } else {
+               if (pci_intr_map_msi(pa, &sc->sc_ih) != 0 &&
+                   pci_intr_map(pa, &sc->sc_ih) != 0) {
+                       printf(", unable to map interrupt\n");
+                       goto shutdown;
+               }
        }
 
-       printf(", %s, address %s\n", pci_intr_string(sc->sc_pc, sc->sc_ih),
+       printf(", %s, %d queue%s, address %s\n",
+           pci_intr_string(sc->sc_pc, sc->sc_ih), ixl_nqueues(sc),
+           (ixl_nqueues(sc) > 1 ? "s" : ""),
            ether_sprintf(sc->sc_ac.ac_enaddr));
 
        if (ixl_hmc(sc) != 0) {
@@ -1585,13 +1607,18 @@ ixl_attach(struct device *parent, struct
        }
 
        sc->sc_ihc = pci_intr_establish(sc->sc_pc, sc->sc_ih,
-           IPL_NET | IPL_MPSAFE, ixl_intr, sc, DEVNAME(sc));
+           IPL_NET | IPL_MPSAFE, ixl_intr0, sc, DEVNAME(sc));
        if (sc->sc_ihc == NULL) {
                printf("%s: unable to establish interrupt handler\n",
                    DEVNAME(sc));
                goto free_scratch;
        }
 
+       if (ixl_setup_msix(sc, pa) != 0) {
+               /* error printed by ixl_setup_msix */
+               goto free_scratch;
+       }
+
        ifp->if_softc = sc;
        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
        ifp->if_xflags = IFXF_MPSAFE;
@@ -1667,6 +1694,9 @@ shutdown:
            BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
 
        ixl_arq_unfill(sc);
+
+       free(sc->sc_qintr, M_DEVBUF, ixl_nqueues(sc) *
+           sizeof(struct ixl_queue_intr));
 free_arq:
        ixl_dmamem_free(sc, &sc->sc_arq);
 free_atq:
@@ -1903,25 +1933,67 @@ ixl_up(struct ixl_softc *sc)
 
        SET(ifp->if_flags, IFF_RUNNING);
 
-       ixl_wr(sc, I40E_PFINT_LNKLST0,
-           (I40E_INTR_NOTX_QUEUE << I40E_PFINT_LNKLST0_FIRSTQ_INDX_SHIFT) |
-           (I40E_QUEUE_TYPE_RX << I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT));
-
-       ixl_wr(sc, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE),
-           (I40E_INTR_NOTX_INTR << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
-           (I40E_ITR_INDEX_RX << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
-           (I40E_INTR_NOTX_RX_QUEUE << I40E_QINT_RQCTL_MSIX0_INDX_SHIFT) |
-           (I40E_INTR_NOTX_QUEUE << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) |
-           (I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT) |
-           I40E_QINT_RQCTL_CAUSE_ENA_MASK);
-
-       ixl_wr(sc, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE),
-           (I40E_INTR_NOTX_INTR << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) |
-           (I40E_ITR_INDEX_TX << I40E_QINT_TQCTL_ITR_INDX_SHIFT) |
-           (I40E_INTR_NOTX_TX_QUEUE << I40E_QINT_TQCTL_MSIX0_INDX_SHIFT) |
-           (I40E_QUEUE_TYPE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) |
-           (I40E_QUEUE_TYPE_RX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT) |
-           I40E_QINT_TQCTL_CAUSE_ENA_MASK);
+       if (sc->sc_qintr == NULL) {
+               ixl_wr(sc, I40E_PFINT_LNKLST0,
+                   (I40E_INTR_NOTX_QUEUE <<
+                    I40E_PFINT_LNKLST0_FIRSTQ_INDX_SHIFT) |
+                   (I40E_QUEUE_TYPE_RX <<
+                    I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT));
+
+               ixl_wr(sc, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE),
+                   (I40E_INTR_NOTX_INTR << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
+                   (I40E_ITR_INDEX_RX << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
+                   (I40E_INTR_NOTX_RX_QUEUE <<
+                    I40E_QINT_RQCTL_MSIX0_INDX_SHIFT) |
+                   (I40E_INTR_NOTX_QUEUE << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) |
+                   (I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT) |
+                   I40E_QINT_RQCTL_CAUSE_ENA_MASK);
+
+               ixl_wr(sc, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE),
+                   (I40E_INTR_NOTX_INTR << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) |
+                   (I40E_ITR_INDEX_TX << I40E_QINT_TQCTL_ITR_INDX_SHIFT) |
+                   (I40E_INTR_NOTX_TX_QUEUE <<
+                    I40E_QINT_TQCTL_MSIX0_INDX_SHIFT) |
+                   (I40E_QUEUE_TYPE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) |
+                   (I40E_QUEUE_TYPE_RX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT) |
+                   I40E_QINT_TQCTL_CAUSE_ENA_MASK);
+       } else {
+               int i;
+               /* vector 0 has no queues */
+               ixl_wr(sc, I40E_PFINT_LNKLST0,
+                   I40E_QUEUE_TYPE_EOL <<
+                   I40E_PFINT_LNKLST0_FIRSTQ_INDX_SHIFT);
+
+               /* queue n is mapped to vector n+1 */
+               for (i = 0; i < ixl_nqueues(sc); i++) {
+                       /* LNKLSTN(i) configures vector i+1 */
+                       ixl_wr(sc, I40E_PFINT_LNKLSTN(i),
+                           (i << I40E_PFINT_LNKLSTN_FIRSTQ_INDX_SHIFT) |
+                           (I40E_QUEUE_TYPE_RX <<
+                            I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT));
+                       ixl_wr(sc, I40E_QINT_RQCTL(i),
+                           ((i+1) << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
+                           (I40E_ITR_INDEX_RX <<
+                            I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
+                           (i << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) |
+                           (I40E_QUEUE_TYPE_TX <<
+                            I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT) |
+                           I40E_QINT_RQCTL_CAUSE_ENA_MASK);
+                       ixl_wr(sc, I40E_QINT_TQCTL(i),
+                           ((i+1) << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) |
+                           (I40E_ITR_INDEX_TX <<
+                            I40E_QINT_TQCTL_ITR_INDX_SHIFT) |
+                           (I40E_QUEUE_TYPE_EOL <<
+                            I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) |
+                           (I40E_QUEUE_TYPE_RX <<
+                            I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT) |
+                           I40E_QINT_TQCTL_CAUSE_ENA_MASK);
+
+                       ixl_wr(sc, I40E_PFINT_ITRN(0, i), 0x7a);
+                       ixl_wr(sc, I40E_PFINT_ITRN(1, i), 0x7a);
+                       ixl_wr(sc, I40E_PFINT_ITRN(2, i), 0);
+               }
+       }
 
        ixl_wr(sc, I40E_PFINT_ITR0(0), 0x7a);
        ixl_wr(sc, I40E_PFINT_ITR0(1), 0x7a);
@@ -2040,6 +2112,9 @@ ixl_down(struct ixl_softc *sc)
                ifq_barrier(ifp->if_ifqs[i]);
 
                timeout_del_barrier(&rxr->rxr_refill);
+
+               if (sc->sc_qintr != NULL)
+                       intr_barrier(sc->sc_qintr[i].ihc);
        }
 
        /* XXX wait at least 400 usec for all tx queues in one go */
@@ -2852,7 +2927,7 @@ ixl_rxrinfo(struct ixl_softc *sc, struct
 }
 
 static int
-ixl_intr(void *xsc)
+ixl_intr0(void *xsc)
 {
        struct ixl_softc *sc = xsc;
        struct ifnet *ifp = &sc->sc_ac.ac_if;
@@ -2881,6 +2956,24 @@ ixl_intr(void *xsc)
        return (rv);
 }
 
+static int
+ixl_intr_queue(void *xqi)
+{
+       struct ixl_queue_intr *qi = xqi;
+       struct ifnet *ifp = &qi->sc->sc_ac.ac_if;
+       int rv = 0;
+
+       rv |= ixl_rxeof(qi->sc, ifp->if_iqs[qi->queue]);
+       rv |= ixl_txeof(qi->sc, ifp->if_ifqs[qi->queue]);
+
+       ixl_wr(qi->sc, I40E_PFINT_DYN_CTLN(qi->queue),
+           I40E_PFINT_DYN_CTLN_INTENA_MASK |
+           I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+           (IXL_NOITR << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT));
+
+       return (rv);
+}
+
 static void
 ixl_link_state_update_done(struct ixl_softc *sc, void *arg)
 {
@@ -4252,6 +4345,38 @@ ixl_arq_unfill(struct ixl_softc *sc)
                    BUS_DMASYNC_POSTREAD);
                ixl_aqb_free(sc, aqb);
        }
+}
+
+static int
+ixl_setup_msix(struct ixl_softc *sc, struct pci_attach_args *pa)
+{
+       pci_chipset_tag_t pc = pa->pa_pc;
+       pci_intr_handle_t ih;
+       int i;
+
+       if (sc->sc_qintr == NULL)
+               return (0);
+
+       for (i = 0; i < ixl_nqueues(sc); i++) {
+               sc->sc_qintr[i].sc = sc;
+               sc->sc_qintr[i].queue = i;
+               if (pci_intr_map_msix(pa, i + 1, &ih))
+                       return (ENODEV);
+
+               snprintf(sc->sc_qintr[i].name, sizeof(sc->sc_qintr[i].name),
+                   "%s:%d", DEVNAME(sc), i);
+
+               sc->sc_qintr[i].ihc = pci_intr_establish(pc, ih,
+                   IPL_NET | IPL_MPSAFE, ixl_intr_queue, &sc->sc_qintr[i],
+                   sc->sc_qintr[i].name);
+
+               ixl_wr(sc, I40E_PFINT_DYN_CTLN(i),
+                   I40E_PFINT_DYN_CTLN_INTENA_MASK |
+                   I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+                   (IXL_NOITR << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT));
+       }
+
+       return (0);
 }
 
 static void

Reply via email to