Diff below allows ix(4) and em(4) to establish two MSI-X handlers.
Multiqueue support is intentionally not included in this diff.

One handler is for the single queue (index ":0") and one for the
link interrupt:

  # vmstat -i |grep ix0
  irq114/ix0:0                 73178178     3758
  irq115/ix0                          2        0

A per-driver toggle allows to switch MSI-X support on/off.  My plan is
to commit with the switch "off" for the moment.  Switching it to "on"
should be better done in the beginning of a release cycle.  However it
is "on" in the diff below for testing purposes.

The em(4) diff doesn't include support for 82575 nor 82574.  If somebody
has such hardware and is interested I can lift the necessary bits from
FreeBSD.  That said em_setup_queues_msix() contains references to 82575
to help diffing with FreeBSD.

The ix(4) diff cannot be committed right now as it breaks the build on
architectures that define pci_intr_map_msix() to (-1).  The compiler
complains that the arguments of the macro are unused.

  /sys/dev/pci/if_ix.c:1789:21: error: unused variable 'dummy'
        [-Werror,-Wunused-variable]
          pci_intr_handle_t        dummy;
                                   ^
  /sys/dev/pci/if_ix.c:1788:26: error: unused variable 'pa'
        [-Werror,-Wunused-variable]
          struct pci_attach_args  *pa = &os->os_pa;

So I'd like to know if that's the way to test if MSI-X can be used:

       /*
        * Try a dummy map, maybe this bus doesn't like MSI, this function
        * has no side effects.
        */
       if (pci_intr_map_msix(pa, 0, &dummy))
               return (0);

I'd appreciate tests on many hardware as possible, especially for em(4).
If you have been testing an earlier version of these diffs, please do
it again :)

Thanks,
Martin

Index: dev/pci/if_em.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em.c,v
retrieving revision 1.347
diff -u -p -r1.347 if_em.c
--- dev/pci/if_em.c     8 Mar 2020 11:43:43 -0000       1.347
+++ dev/pci/if_em.c     17 Mar 2020 11:18:01 -0000
@@ -233,6 +233,7 @@ void em_defer_attach(struct device*);
 int  em_detach(struct device *, int);
 int  em_activate(struct device *, int);
 int  em_intr(void *);
+int  em_allocate_legacy(struct em_softc *);
 void em_start(struct ifqueue *);
 int  em_ioctl(struct ifnet *, u_long, caddr_t);
 void em_watchdog(struct ifnet *);
@@ -290,6 +291,13 @@ void em_flush_tx_ring(struct em_queue *)
 void em_flush_rx_ring(struct em_queue *);
 void em_flush_desc_rings(struct em_softc *);
 
+/* MSIX/Multiqueue functions */
+int  em_allocate_msix(struct em_softc *);
+int  em_setup_queues_msix(struct em_softc *);
+int  em_queue_intr_msix(void *);
+int  em_link_intr_msix(void *);
+void em_enable_queue_intr_msix(struct em_queue *);
+
 /*********************************************************************
  *  OpenBSD Device Interface Entry Points
  *********************************************************************/
@@ -304,6 +312,7 @@ struct cfdriver em_cd = {
 };
 
 static int em_smart_pwr_down = FALSE;
+int em_enable_msix = 1;
 
 /*********************************************************************
  *  Device identification routine
@@ -919,6 +928,14 @@ em_init(void *arg)
        }
        em_initialize_receive_unit(sc);
 
+       if (sc->msix) {
+               if (em_setup_queues_msix(sc)) {
+                       printf("%s: Can't setup msix queues\n", DEVNAME(sc));
+                       splx(s);
+                       return;
+               }
+       }
+
        /* Program promiscuous mode and multicast filters. */
        em_iff(sc);
 
@@ -1617,10 +1634,7 @@ int
 em_allocate_pci_resources(struct em_softc *sc)
 {
        int             val, rid;
-       pci_intr_handle_t       ih;
-       const char              *intrstr = NULL;
        struct pci_attach_args *pa = &sc->osdep.em_pa;
-       pci_chipset_tag_t       pc = pa->pa_pc;
        struct em_queue        *que = NULL;
 
        val = pci_conf_read(pa->pa_pc, pa->pa_tag, EM_MMBA);
@@ -1691,6 +1705,9 @@ em_allocate_pci_resources(struct em_soft
                }
         }
 
+       sc->osdep.dev = (struct device *)sc;
+       sc->hw.back = &sc->osdep;
+
        /* Only one queue for the moment. */
        que = malloc(sizeof(struct em_queue), M_DEVBUF, M_NOWAIT | M_ZERO);
        if (que == NULL) {
@@ -1703,29 +1720,10 @@ em_allocate_pci_resources(struct em_soft
 
        sc->queues = que;
        sc->num_queues = 1;
+       sc->msix = 0;
        sc->legacy_irq = 0;
-       if (pci_intr_map_msi(pa, &ih)) {
-               if (pci_intr_map(pa, &ih)) {
-                       printf(": couldn't map interrupt\n");
-                       return (ENXIO);
-               }
-               sc->legacy_irq = 1;
-       }
-
-       sc->osdep.dev = (struct device *)sc;
-       sc->hw.back = &sc->osdep;
-
-       intrstr = pci_intr_string(pc, ih);
-       sc->sc_intrhand = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
-           em_intr, sc, DEVNAME(sc));
-       if (sc->sc_intrhand == NULL) {
-               printf(": couldn't establish interrupt");
-               if (intrstr != NULL)
-                       printf(" at %s", intrstr);
-               printf("\n");
+       if (em_allocate_msix(sc) && em_allocate_legacy(sc))
                return (ENXIO);
-       }
-       printf(": %s", intrstr);
 
        /*
         * the ICP_xxxx device has multiple, duplicate register sets for
@@ -1784,6 +1782,15 @@ em_free_pci_resources(struct em_softc *s
                que->tx.sc_tx_desc_ring = NULL;
                em_dma_free(sc, &que->tx.sc_tx_dma);
        }
+       if (que->tag)
+               pci_intr_disestablish(pc, que->tag);
+       que->tag = NULL;
+       que->eims = 0;
+       que->me = 0;
+       que->sc = NULL;
+       sc->legacy_irq = 0;
+       sc->msix_linkvec = 0;
+       sc->msix_queuesmask = 0;
        if (sc->queues)
                free(sc->queues, M_DEVBUF,
                    sc->num_queues * sizeof(struct em_queue));
@@ -1971,6 +1978,7 @@ em_setup_interface(struct em_softc *sc)
 
        if_attach(ifp);
        ether_ifattach(ifp);
+       em_enable_intr(sc);
 }
 
 int
@@ -3017,7 +3025,16 @@ em_enable_hw_vlans(struct em_softc *sc)
 void
 em_enable_intr(struct em_softc *sc)
 {
-       E1000_WRITE_REG(&sc->hw, IMS, (IMS_ENABLE_MASK));
+       uint32_t mask;
+
+       if (sc->msix) {
+               mask = sc->msix_queuesmask | sc->msix_linkmask;
+               E1000_WRITE_REG(&sc->hw, EIAC, mask);
+               E1000_WRITE_REG(&sc->hw, EIAM, mask);
+               E1000_WRITE_REG(&sc->hw, EIMS, mask);
+               E1000_WRITE_REG(&sc->hw, IMS, E1000_IMS_LSC);
+       } else
+               E1000_WRITE_REG(&sc->hw, IMS, (IMS_ENABLE_MASK));
 }
 
 void
@@ -3031,8 +3048,10 @@ em_disable_intr(struct em_softc *sc)
         * For this to work correctly the Sequence error interrupt had
         * to be enabled all the time.
         */
-
-       if (sc->hw.mac_type == em_82542_rev2_0)
+       if (sc->msix) {
+               E1000_WRITE_REG(&sc->hw, EIMC, ~0);
+               E1000_WRITE_REG(&sc->hw, EIAC, 0);
+       } else if (sc->hw.mac_type == em_82542_rev2_0)
                E1000_WRITE_REG(&sc->hw, IMC, (0xffffffff & ~E1000_IMC_RXSEQ));
        else
                E1000_WRITE_REG(&sc->hw, IMC, 0xffffffff);
@@ -3506,6 +3525,264 @@ em_print_hw_stats(struct em_softc *sc)
 }
 #endif
 #endif /* !SMALL_KERNEL */
+
+int
+em_allocate_legacy(struct em_softc *sc)
+{
+       pci_intr_handle_t        ih;
+       const char              *intrstr = NULL;
+       struct pci_attach_args  *pa = &sc->osdep.em_pa;
+       pci_chipset_tag_t        pc = pa->pa_pc;
+
+       if (pci_intr_map_msi(pa, &ih)) {
+               if (pci_intr_map(pa, &ih)) {
+                       printf(": couldn't map interrupt\n");
+                       return (ENXIO);
+               }
+               sc->legacy_irq = 1;
+       }
+
+       intrstr = pci_intr_string(pc, ih);
+       sc->sc_intrhand = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
+           em_intr, sc, DEVNAME(sc));
+       if (sc->sc_intrhand == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               return (ENXIO);
+       }
+       printf(": %s", intrstr);
+
+       return (0);
+}
+
+int
+em_allocate_msix(struct em_softc *sc)
+{
+       pci_intr_handle_t        ih;
+       const char              *intrstr = NULL;
+       struct pci_attach_args  *pa = &sc->osdep.em_pa;
+       pci_chipset_tag_t        pc = pa->pa_pc;
+       struct em_queue         *que = sc->queues; /* Use only first queue. */
+       int                      vec;
+
+       if (!em_enable_msix)
+               return (ENODEV);
+
+       switch (sc->hw.mac_type) {
+       case em_82576:
+       case em_82580:
+       case em_i350:
+       case em_i210:
+               break;
+       default:
+               return (ENODEV);
+       }
+
+       vec = 0;
+       if (pci_intr_map_msix(pa, vec, &ih))
+               return (ENODEV);
+       sc->msix = 1;
+
+       que->me = vec;
+       que->eims = 1 << vec;
+       snprintf(que->name, sizeof(que->name), "%s:%d", DEVNAME(sc), vec);
+
+       intrstr = pci_intr_string(pc, ih);
+       que->tag = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
+           em_queue_intr_msix, que, que->name);
+       if (que->tag == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               return (ENXIO);
+       }
+
+       /* Setup linkvector, use last queue vector + 1 */
+       vec++;
+       sc->msix_linkvec = vec;
+       if (pci_intr_map_msix(pa, sc->msix_linkvec, &ih)) {
+               printf(": couldn't map link vector\n");
+               return (ENXIO);
+       }
+
+       intrstr = pci_intr_string(pc, ih);
+       sc->sc_intrhand = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
+           em_link_intr_msix, sc, DEVNAME(sc));
+       if (sc->sc_intrhand == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               return (ENXIO);
+       }
+       printf(", %s, %d queue%s", intrstr, vec, (vec > 1) ? "s" : "");
+
+       return (0);
+}
+
+/*
+ * Interrupt for a specific queue, (not link interrupts). The EICR bit which
+ * maps to the EIMS bit expresses both RX and TX, therefore we can't
+ * distringuish if this is a RX completion of TX completion and must do both.
+ * The bits in EICR are autocleared and we _cannot_ read EICR.
+ */
+int
+em_queue_intr_msix(void *vque)
+{
+       struct em_queue *que = vque;
+       struct em_softc *sc = que->sc;
+       struct ifnet   *ifp = &sc->sc_ac.ac_if;
+
+       if (ifp->if_flags & IFF_RUNNING) {
+               em_txeof(que);
+               if (em_rxeof(que))
+                       em_rxrefill(que);
+       }
+
+       em_enable_queue_intr_msix(que);
+
+       return (1);
+}
+
+int
+em_link_intr_msix(void *arg)
+{
+       struct em_softc *sc = arg;
+       uint32_t icr;
+
+       icr = E1000_READ_REG(&sc->hw, ICR);
+
+       /* Link status change */
+       if (icr & E1000_ICR_LSC) {
+               KERNEL_LOCK();
+               sc->hw.get_link_status = 1;
+               em_check_for_link(&sc->hw);
+               em_update_link_status(sc);
+               KERNEL_UNLOCK();
+       }
+
+       /* Re-arm unconditionally */
+       E1000_WRITE_REG(&sc->hw, IMS, E1000_ICR_LSC);
+       E1000_WRITE_REG(&sc->hw, EIMS, sc->msix_linkmask);
+
+       return (1);
+}
+
+/*
+ * Maps queues into msix interrupt vectors.
+ */
+int
+em_setup_queues_msix(struct em_softc *sc)
+{
+       struct em_queue *que = sc->queues; /* Use only first queue. */
+       uint32_t ivar, newitr, index;
+
+       KASSERT(sc->msix);
+
+       /* First turn on RSS capability */
+       if (sc->hw.mac_type != em_82575)
+               E1000_WRITE_REG(&sc->hw, GPIE,
+                   E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME |
+                   E1000_GPIE_PBA | E1000_GPIE_NSICR);
+
+       /* Turn on MSIX */
+       switch (sc->hw.mac_type) {
+       case em_82580:
+       case em_i350:
+       case em_i210:
+               /* RX entries */
+               /*
+                * Note, this maps Queues into MSIX vectors, it works fine.
+                * The funky calculation of offsets and checking if que->me is
+                * odd is due to the weird register distribution, the datasheet
+                * explains it well.
+                */
+               index = que->me >> 1;
+               ivar = E1000_READ_REG_ARRAY(&sc->hw, IVAR0, index);
+               if (que->me & 1) {
+                       ivar &= 0xFF00FFFF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 16;
+               } else {
+                       ivar &= 0xFFFFFF00;
+                       ivar |= que->me | E1000_IVAR_VALID;
+               }
+               E1000_WRITE_REG_ARRAY(&sc->hw, IVAR0, index, ivar);
+
+               /* TX entries */
+               index = que->me >> 1;
+               ivar = E1000_READ_REG_ARRAY(&sc->hw, IVAR0, index);
+               if (que->me & 1) {
+                       ivar &= 0x00FFFFFF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 24;
+               } else {
+                       ivar &= 0xFFFF00FF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 8;
+               }
+               E1000_WRITE_REG_ARRAY(&sc->hw, IVAR0, index, ivar);
+               sc->msix_queuesmask |= que->eims;
+
+               /* And for the link interrupt */
+               ivar = (sc->msix_linkvec | E1000_IVAR_VALID) << 8;
+               sc->msix_linkmask = 1 << sc->msix_linkvec;
+               E1000_WRITE_REG(&sc->hw, IVAR_MISC, ivar);
+               break;
+       case em_82576:
+               /* RX entries */
+               index = que->me & 0x7; /* Each IVAR has two entries */
+               ivar = E1000_READ_REG_ARRAY(&sc->hw, IVAR0, index);
+               if (que->me < 8) {
+                       ivar &= 0xFFFFFF00;
+                       ivar |= que->me | E1000_IVAR_VALID;
+               } else {
+                       ivar &= 0xFF00FFFF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 16;
+               }
+               E1000_WRITE_REG_ARRAY(&sc->hw, IVAR0, index, ivar);
+               sc->msix_queuesmask |= que->eims;
+               /* TX entries */
+               index = que->me & 0x7; /* Each IVAR has two entries */
+               ivar = E1000_READ_REG_ARRAY(&sc->hw, IVAR0, index);
+               if (que->me < 8) {
+                       ivar &= 0xFFFF00FF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 8;
+               } else {
+                       ivar &= 0x00FFFFFF;
+                       ivar |= (que->me | E1000_IVAR_VALID) << 24;
+               }
+               E1000_WRITE_REG_ARRAY(&sc->hw, IVAR0, index, ivar);
+               sc->msix_queuesmask |= que->eims;
+
+               /* And for the link interrupt */
+               ivar = (sc->msix_linkvec | E1000_IVAR_VALID) << 8;
+               sc->msix_linkmask = 1 << sc->msix_linkvec;
+               E1000_WRITE_REG(&sc->hw, IVAR_MISC, ivar);
+               break;
+       default:
+               panic("unsupported mac");
+               break;
+       }
+
+       /* Set the starting interrupt rate */
+       newitr = (4000000 / MAX_INTS_PER_SEC) & 0x7FFC;
+
+       if (sc->hw.mac_type == em_82575)
+               newitr |= newitr << 16;
+       else
+               newitr |= E1000_EITR_CNT_IGNR;
+
+       E1000_WRITE_REG(&sc->hw, EITR(que->me), newitr);
+
+       return (0);
+}
+
+void
+em_enable_queue_intr_msix(struct em_queue *que)
+{
+       E1000_WRITE_REG(&que->sc->hw, EIMS, que->eims);
+}
 
 int
 em_allocate_desc_rings(struct em_softc *sc)
Index: dev/pci/if_em.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em.h,v
retrieving revision 1.75
diff -u -p -r1.75 if_em.h
--- dev/pci/if_em.h     20 Feb 2020 09:32:49 -0000      1.75
+++ dev/pci/if_em.h     9 Mar 2020 10:48:15 -0000
@@ -358,7 +358,9 @@ struct em_softc;
 struct em_queue {
        struct em_softc         *sc;
        uint32_t                 me;    /* queue index, also msix vector */
-
+       uint32_t                 eims;  /* msix only */
+       void                    *tag;   /* NULL in legacy, check sc_intrhand */
+       char                     name[8];
        struct em_tx             tx;
        struct em_rx             rx;
 
@@ -433,6 +435,10 @@ struct em_softc {
        boolean_t       pcix_82544;
        struct em_hw_stats stats;
 
+       int                      msix;
+       uint32_t                 msix_linkvec;
+       uint32_t                 msix_linkmask;
+       uint32_t                 msix_queuesmask;
        int                      num_queues;
        struct em_queue         *queues;
 };
Index: dev/pci/if_em_hw.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_em_hw.h,v
retrieving revision 1.81
diff -u -p -r1.81 if_em_hw.h
--- dev/pci/if_em_hw.h  8 Mar 2020 11:43:43 -0000       1.81
+++ dev/pci/if_em_hw.h  17 Mar 2020 10:58:49 -0000
@@ -2033,6 +2033,11 @@ struct em_hw {
 #define E1000_RXDCTL_THRESH_UNIT_DESC 0x1000000
 #define E1000_RXDCTL_QUEUE_ENABLE 0x2000000
 
+#define E1000_EITR_ITR_INT_MASK        0x0000FFFF
+/* E1000_EITR_CNT_IGNR is only for 82576 and newer */
+#define E1000_EITR_CNT_IGNR    0x80000000 /* Don't reset counters on write */
+#define E1000_EITR_INTERVAL    0x00007FFC
+
 /* Transmit Descriptor Control */
 #define E1000_TXDCTL_PTHRESH 0x000000FF /* TXDCTL Prefetch Threshold */
 #define E1000_TXDCTL_HTHRESH 0x0000FF00 /* TXDCTL Host Threshold */
Index: dev/pci/if_ix.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_ix.c,v
retrieving revision 1.161
diff -u -p -r1.161 if_ix.c
--- dev/pci/if_ix.c     2 Mar 2020 01:59:01 -0000       1.161
+++ dev/pci/if_ix.c     17 Mar 2020 11:41:58 -0000
@@ -114,6 +114,8 @@ int ixgbe_media_change(struct ifnet *);
 void   ixgbe_identify_hardware(struct ix_softc *);
 int    ixgbe_allocate_pci_resources(struct ix_softc *);
 int    ixgbe_allocate_legacy(struct ix_softc *);
+int    ixgbe_allocate_msix(struct ix_softc *);
+int    ixgbe_setup_msix(struct ix_softc *);
 int    ixgbe_allocate_queues(struct ix_softc *);
 void   ixgbe_free_pci_resources(struct ix_softc *);
 void   ixgbe_local_timer(void *);
@@ -140,6 +142,7 @@ void        ixgbe_initialize_rss_mapping(struct
 int    ixgbe_rxfill(struct rx_ring *);
 void   ixgbe_rxrefill(void *);
 
+int    ixgbe_intr(struct ix_softc *sc);
 void   ixgbe_enable_intr(struct ix_softc *);
 void   ixgbe_disable_intr(struct ix_softc *);
 void   ixgbe_update_stats_counters(struct ix_softc *);
@@ -172,11 +175,16 @@ void      ixgbe_handle_msf(struct ix_softc *)
 void   ixgbe_handle_phy(struct ix_softc *);
 
 /* Legacy (single vector interrupt handler */
-int    ixgbe_intr(void *);
+int    ixgbe_legacy_intr(void *);
 void   ixgbe_enable_queue(struct ix_softc *, uint32_t);
+void   ixgbe_enable_queues(struct ix_softc *);
 void   ixgbe_disable_queue(struct ix_softc *, uint32_t);
 void   ixgbe_rearm_queue(struct ix_softc *, uint32_t);
 
+/* MSI-X (multiple vectors interrupt handlers)  */
+int    ixgbe_link_intr(void *);
+int    ixgbe_queue_intr(void *);
+
 /*********************************************************************
  *  OpenBSD Device Interface Entry Points
  *********************************************************************/
@@ -190,6 +198,7 @@ struct cfattach ix_ca = {
 };
 
 int ixgbe_smart_speed = ixgbe_smart_speed_on;
+int ixgbe_enable_msix = 1;
 
 /*********************************************************************
  *  Device identification routine
@@ -292,7 +301,10 @@ ixgbe_attach(struct device *parent, stru
        bcopy(sc->hw.mac.addr, sc->arpcom.ac_enaddr,
            IXGBE_ETH_LENGTH_OF_ADDRESS);
 
-       error = ixgbe_allocate_legacy(sc);
+       if (sc->msix > 1)
+               error = ixgbe_allocate_msix(sc);
+       else
+               error = ixgbe_allocate_legacy(sc);
        if (error)
                goto err_late;
 
@@ -524,6 +536,7 @@ ixgbe_ioctl(struct ifnet * ifp, u_long c
                        ixgbe_disable_intr(sc);
                        ixgbe_iff(sc);
                        ixgbe_enable_intr(sc);
+                       ixgbe_enable_queues(sc);
                }
                error = 0;
        }
@@ -819,6 +832,9 @@ ixgbe_init(void *arg)
                itr |= IXGBE_EITR_LLI_MOD | IXGBE_EITR_CNT_WDIS;
        IXGBE_WRITE_REG(&sc->hw, IXGBE_EITR(0), itr);
 
+       /* Set moderation on the Link interrupt */
+       IXGBE_WRITE_REG(&sc->hw, IXGBE_EITR(sc->linkvec), IXGBE_LINK_ITR);
+
        /* Enable power to the phy */
        if (sc->hw.phy.ops.set_phy_power)
                sc->hw.phy.ops.set_phy_power(&sc->hw, TRUE);
@@ -834,6 +850,7 @@ ixgbe_init(void *arg)
 
        /* And now turn on interrupts */
        ixgbe_enable_intr(sc);
+       ixgbe_enable_queues(sc);
 
        /* Now inform the stack we're ready */
        ifp->if_flags |= IFF_RUNNING;
@@ -964,6 +981,16 @@ ixgbe_enable_queue(struct ix_softc *sc, 
 }
 
 void
+ixgbe_enable_queues(struct ix_softc *sc)
+{
+       struct ix_queue *que;
+       int i;
+
+       for (i = 0, que = sc->queues; i < sc->num_queues; i++, que++)
+               ixgbe_enable_queue(sc, que->msix);
+}
+
+void
 ixgbe_disable_queue(struct ix_softc *sc, uint32_t vector)
 {
        uint64_t queue = 1ULL << vector;
@@ -982,6 +1009,41 @@ ixgbe_disable_queue(struct ix_softc *sc,
        }
 }
 
+/*
+ * MSIX Interrupt Handlers
+ */
+int
+ixgbe_link_intr(void *vsc)
+{
+       struct ix_softc *sc = (struct ix_softc *)vsc;
+
+       return ixgbe_intr(sc);
+}
+
+int
+ixgbe_queue_intr(void *vque)
+{
+       struct ix_queue *que = vque;
+       struct ix_softc *sc = que->sc;
+       struct ifnet    *ifp = &sc->arpcom.ac_if;
+       struct tx_ring  *txr = sc->tx_rings;
+
+       if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+               ixgbe_rxeof(que);
+               ixgbe_txeof(txr);
+               if (ixgbe_rxfill(que->rxr)) {
+                       /* Advance the Rx Queue "Tail Pointer" */
+                       IXGBE_WRITE_REG(&sc->hw, IXGBE_RDT(que->rxr->me),
+                           que->rxr->last_desc_filled);
+               } else
+                       timeout_add(&sc->rx_refill, 1);
+       }
+
+       ixgbe_enable_queue(sc, que->msix);
+
+       return (1);
+}
+
 /*********************************************************************
  *
  *  Legacy Interrupt Service routine
@@ -989,29 +1051,24 @@ ixgbe_disable_queue(struct ix_softc *sc,
  **********************************************************************/
 
 int
-ixgbe_intr(void *arg)
+ixgbe_legacy_intr(void *arg)
 {
        struct ix_softc *sc = (struct ix_softc *)arg;
-       struct ix_queue *que = sc->queues;
        struct ifnet    *ifp = &sc->arpcom.ac_if;
        struct tx_ring  *txr = sc->tx_rings;
-       struct ixgbe_hw *hw = &sc->hw;
-       uint32_t         reg_eicr, mod_mask, msf_mask;
-       int              i, refill = 0;
+       int rv;
 
-       reg_eicr = IXGBE_READ_REG(&sc->hw, IXGBE_EICR);
-       if (reg_eicr == 0) {
-               ixgbe_enable_intr(sc);
+       rv = ixgbe_intr(sc);
+       if (rv == 0) {
+               ixgbe_enable_queues(sc);
                return (0);
        }
 
        if (ISSET(ifp->if_flags, IFF_RUNNING)) {
+               struct ix_queue *que = sc->queues;
+
                ixgbe_rxeof(que);
                ixgbe_txeof(txr);
-               refill = 1;
-       }
-
-       if (refill) {
                if (ixgbe_rxfill(que->rxr)) {
                        /* Advance the Rx Queue "Tail Pointer" */
                        IXGBE_WRITE_REG(&sc->hw, IXGBE_RDT(que->rxr->me),
@@ -1020,6 +1077,22 @@ ixgbe_intr(void *arg)
                        timeout_add(&sc->rx_refill, 1);
        }
 
+       return (rv);
+}
+
+int
+ixgbe_intr(struct ix_softc *sc)
+{
+       struct ifnet    *ifp = &sc->arpcom.ac_if;
+       struct ixgbe_hw *hw = &sc->hw;
+       uint32_t         reg_eicr, mod_mask, msf_mask;
+
+       reg_eicr = IXGBE_READ_REG(&sc->hw, IXGBE_EICR);
+       if (reg_eicr == 0) {
+               ixgbe_enable_intr(sc);
+               return (0);
+       }
+
        /* Link status change */
        if (reg_eicr & IXGBE_EICR_LSC) {
                KERNEL_LOCK();
@@ -1090,9 +1163,6 @@ ixgbe_intr(void *arg)
                KERNEL_UNLOCK();
        }
 
-       for (i = 0; i < sc->num_queues; i++, que++)
-               ixgbe_enable_queue(sc, que->msix);
-
        return (1);
 }
 
@@ -1626,7 +1696,7 @@ ixgbe_allocate_legacy(struct ix_softc *s
 
        intrstr = pci_intr_string(pc, ih);
        sc->tag = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
-           ixgbe_intr, sc, sc->dev.dv_xname);
+           ixgbe_legacy_intr, sc, sc->dev.dv_xname);
        if (sc->tag == NULL) {
                printf(": couldn't establish interrupt");
                if (intrstr != NULL)
@@ -1642,6 +1712,92 @@ ixgbe_allocate_legacy(struct ix_softc *s
        return (0);
 }
 
+/*********************************************************************
+ *
+ *  Setup the MSI-X Interrupt handlers
+ *
+ **********************************************************************/
+int
+ixgbe_allocate_msix(struct ix_softc *sc)
+{
+       struct ixgbe_osdep      *os = &sc->osdep;
+       struct pci_attach_args  *pa  = &os->os_pa;
+       int                      vec, error = 0;
+       struct ix_queue         *que = sc->queues;
+       const char              *intrstr = NULL;
+       pci_chipset_tag_t       pc = pa->pa_pc;
+       pci_intr_handle_t       ih;
+
+       vec = 0;
+       if (pci_intr_map_msix(pa, vec, &ih)) {
+               printf(": couldn't map interrupt\n");
+               return (ENXIO);
+       }
+
+       que->msix = vec;
+       snprintf(que->name, sizeof(que->name), "%s:%d", sc->dev.dv_xname, vec);
+
+       intrstr = pci_intr_string(pc, ih);
+       que->tag = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
+           ixgbe_queue_intr, que, que->name);
+       if (que->tag == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               return (ENXIO);
+       }
+
+       /* Now the link status/control last MSI-X vector */
+       vec++;
+       if (pci_intr_map_msix(pa, vec, &ih)) {
+               printf(": couldn't map link vector\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       intrstr = pci_intr_string(pc, ih);
+       sc->tag = pci_intr_establish(pc, ih, IPL_NET | IPL_MPSAFE,
+           ixgbe_link_intr, sc, sc->dev.dv_xname);
+       if (sc->tag == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       sc->linkvec = vec;
+       printf(", %s, %d queue%s", intrstr, vec, (vec > 1) ? "s" : "");
+
+       return (0);
+fail:
+       pci_intr_disestablish(pc, que->tag);
+       que->tag = NULL;
+       return (error);
+}
+
+int
+ixgbe_setup_msix(struct ix_softc *sc)
+{
+       struct ixgbe_osdep      *os = &sc->osdep;
+       struct pci_attach_args  *pa = &os->os_pa;
+       pci_intr_handle_t        dummy;
+
+       if (!ixgbe_enable_msix)
+               return (0);
+
+       /*
+        * Try a dummy map, maybe this bus doesn't like MSI, this function
+        * has no side effects.
+        */
+       if (pci_intr_map_msix(pa, 0, &dummy))
+               return (0);
+
+       return (2); /* queue vector + link vector */
+}
+
 int
 ixgbe_allocate_pci_resources(struct ix_softc *sc)
 {
@@ -1666,10 +1822,8 @@ ixgbe_allocate_pci_resources(struct ix_s
        sc->num_queues = 1;
        sc->hw.back = os;
 
-#ifdef notyet
        /* Now setup MSI or MSI/X, return us the number of supported vectors. */
        sc->msix = ixgbe_setup_msix(sc);
-#endif
 
        return (0);
 }
@@ -3085,9 +3239,7 @@ void
 ixgbe_enable_intr(struct ix_softc *sc)
 {
        struct ixgbe_hw *hw = &sc->hw;
-       struct ix_queue *que = sc->queues;
        uint32_t        mask, fwsm;
-       int i;
 
        mask = (IXGBE_EIMS_ENABLE_MASK & ~IXGBE_EIMS_RTX_QUEUE);
        /* Enable Fan Failure detection */
@@ -3135,14 +3287,6 @@ ixgbe_enable_intr(struct ix_softc *sc)
                IXGBE_WRITE_REG(hw, IXGBE_EIAC, mask);
        }
 
-       /*
-        * Now enable all queues, this is done separately to
-        * allow for handling the extended (beyond 32) MSIX
-        * vectors that can be used by 82599
-        */
-       for (i = 0; i < sc->num_queues; i++, que++)
-               ixgbe_enable_queue(sc, que->msix);
-
        IXGBE_WRITE_FLUSH(hw);
 }
 
@@ -3258,15 +3402,11 @@ ixgbe_set_ivar(struct ix_softc *sc, uint
 void
 ixgbe_configure_ivars(struct ix_softc *sc)
 {
-#if notyet
        struct ix_queue *que = sc->queues;
        uint32_t newitr;
        int i;
 
-       if (ixgbe_max_interrupt_rate > 0)
-               newitr = (4000000 / ixgbe_max_interrupt_rate) & 0x0FF8;
-       else
-               newitr = 0;
+       newitr = (4000000 / IXGBE_INTS_PER_SEC) & 0x0FF8;
 
        for (i = 0; i < sc->num_queues; i++, que++) {
                /* First the RX queue entry */
@@ -3280,7 +3420,6 @@ ixgbe_configure_ivars(struct ix_softc *s
 
        /* For the Link interrupt */
        ixgbe_set_ivar(sc, 1, sc->linkvec, -1);
-#endif
 }
 
 /*
Index: dev/pci/if_ix.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_ix.h,v
retrieving revision 1.37
diff -u -p -r1.37 if_ix.h
--- dev/pci/if_ix.h     2 Mar 2020 01:59:01 -0000       1.37
+++ dev/pci/if_ix.h     2 Mar 2020 09:28:59 -0000
@@ -120,6 +120,7 @@
  * Interrupt Moderation parameters
  */
 #define IXGBE_INTS_PER_SEC             8000
+#define IXGBE_LINK_ITR                 1000
 
 struct ixgbe_tx_buf {
        uint32_t                eop_index;
@@ -154,6 +155,8 @@ struct ix_queue {
        uint32_t                msix;           /* This queue's MSIX vector */
        uint32_t                eims;           /* This queue's EIMS bit */
        uint32_t                eitr_setting;
+       char                    name[8];
+       pci_intr_handle_t       ih;
        void                    *tag;
        struct tx_ring          *txr;
        struct rx_ring          *rxr;

Reply via email to