polling: Implement direct input support.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 23 Sep 2017 03:19:26 +0000 (11:19 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 24 Sep 2017 04:27:28 +0000 (12:27 +0800)
When "direct input" is enabled by driver, driver's RX polling handler
will run ethernet/ip/tcp processing directly, which avoids cache-miss
on mbufs themselves.  Currently it is enabled on ix(4) by default.

The normal IP forwarding performance is improved by %12, while the fast
IP forwarding performance is improved by 10%.  13.2Mpps is achieved for
dual side IP forwarding!

1 request/connection HTTP/1.1 performance and avg-latency stay same,
but the latency is further stablized:
1K  5.20ms  -> 4.60ms
8K  6.43ms  -> 5.76ms
16K 16.30ms -> 14.90ms

sbin/ifconfig/ifconfig.c
share/man/man4/ix.4
sys/dev/netif/ix/if_ix.c
sys/dev/netif/ix/if_ix.h
sys/net/if.h
sys/net/if_ethersubr.c
sys/net/if_poll.c

index 142c0b2..a119be8 100644 (file)
@@ -862,7 +862,7 @@ rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
 #define        IFFBITS \
 "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
 "\10NOARP\11PROMISC\12ALLMULTI\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
-"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25NPOLLING"
+"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25NPOLLING\26IDIRECT"
 
 #define        IFCAPBITS \
 "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7RSS" \
index 90e394a..9a54427 100644 (file)
@@ -31,7 +31,7 @@
 .\"
 .\" $FreeBSD: src/share/man/man4/ixgbe.4,v 1.2 2008/06/17 21:14:02 brueffer Exp $
 .\"
-.Dd May 8, 2017
+.Dd September 24, 2017
 .Dt IX 4
 .Os
 .Sh NAME
@@ -260,6 +260,15 @@ force-txpause (force PAUSE transmission),
 force-full (forcefully enable PAUSE reception and transmission),
 force-none (forcefully disable flow control PAUSE operation).
 Default is none.
+.It Va hw.ix.direct_input
+By default,
+if the
+.Xr polling 4
+is enabled,
+the driver will handle input packets directly,
+instead of queuing input packets for further processing.
+The direct input packets handling improves performance and
+stablize latency.
 .El
 .Sh MIB Variables
 A number of per-interface variables are implemented in the
@@ -347,6 +356,11 @@ Setting this value too high will make device drop incoming packets.
 Setting this value too low will hurt overall reception performance
 due to the frequent hardware register writing.
 The default value is 32.
+.It Va direct_input
+See
+.Va hw.ix.direct_input .
+.Xr polling 4
+needs to be turned off and turned on again to make this variable take effect.
 .El
 .Sh SEE ALSO
 .Xr altq 4 ,
index da011d0..a5b1eb5 100644 (file)
@@ -47,6 +47,7 @@
 #include <sys/sockio.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
+#include <sys/taskqueue.h>
 
 #include <net/bpf.h>
 #include <net/ethernet.h>
@@ -152,6 +153,7 @@ static void ix_timer(void *);
 #ifdef IFPOLL_ENABLE
 static void    ix_npoll(struct ifnet *, struct ifpoll_info *);
 static void    ix_npoll_rx(struct ifnet *, void *, int);
+static void    ix_npoll_rx_direct(struct ifnet *, void *, int);
 static void    ix_npoll_tx(struct ifnet *, void *, int);
 static void    ix_npoll_status(struct ifnet *);
 #endif
@@ -173,6 +175,9 @@ static int  ix_sysctl_sts_intr_rate(SYSCTL_HANDLER_ARGS);
 static void     ix_add_hw_stats(struct ix_softc *);
 #endif
 
+static void    ix_watchdog_reset(struct ix_softc *);
+static void    ix_watchdog_task(void *, int);
+static void    ix_sync_netisr(struct ix_softc *, int);
 static void    ix_slot_info(struct ix_softc *);
 static int     ix_alloc_rings(struct ix_softc *);
 static void    ix_free_rings(struct ix_softc *);
@@ -283,6 +288,7 @@ static int  ix_txr = 0;
 static int     ix_txd = IX_PERF_TXD;
 static int     ix_rxd = IX_PERF_RXD;
 static int     ix_unsupported_sfp = 0;
+static int     ix_direct_input = 1;
 
 static char    ix_flowctrl[IFM_ETH_FC_STRLEN] = IFM_ETH_FC_NONE;
 
@@ -294,6 +300,7 @@ TUNABLE_INT("hw.ix.txd", &ix_txd);
 TUNABLE_INT("hw.ix.rxd", &ix_rxd);
 TUNABLE_INT("hw.ix.unsupported_sfp", &ix_unsupported_sfp);
 TUNABLE_STR("hw.ix.flow_ctrl", ix_flowctrl, sizeof(ix_flowctrl));
+TUNABLE_INT("hw.ix.direct_input", &ix_direct_input);
 
 /*
  * Smart speed setting, default to on.  This only works
@@ -390,6 +397,9 @@ ix_attach(device_t dev)
        /* Save frame size */
        sc->max_frame_size = ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN;
 
+       sc->direct_input = ix_direct_input;
+       TASK_INIT(&sc->wdog_task, 0, ix_watchdog_task, sc);
+
        callout_init_mp(&sc->timer);
        lwkt_serialize_init(&sc->main_serialize);
 
@@ -546,6 +556,9 @@ ix_detach(device_t dev)
                struct ifnet *ifp = &sc->arpcom.ac_if;
                uint32_t ctrl_ext;
 
+               ix_sync_netisr(sc, IFF_UP);
+               taskqueue_drain(taskqueue_thread[0], &sc->wdog_task);
+
                ifnet_serialize_all(ifp);
 
                ix_powerdown(sc);
@@ -601,6 +614,9 @@ ix_shutdown(device_t dev)
        struct ix_softc *sc = device_get_softc(dev);
        struct ifnet *ifp = &sc->arpcom.ac_if;
 
+       ix_sync_netisr(sc, IFF_UP);
+       taskqueue_drain(taskqueue_thread[0], &sc->wdog_task);
+
        ifnet_serialize_all(ifp);
        ix_powerdown(sc);
        ifnet_deserialize_all(ifp);
@@ -3918,13 +3934,55 @@ ix_free_rings(struct ix_softc *sc)
                bus_dma_tag_destroy(sc->parent_tag);
 }
 
+static void
+ix_watchdog_reset(struct ix_softc *sc)
+{
+       int i;
+
+       ASSERT_IFNET_SERIALIZED_ALL(&sc->arpcom.ac_if);
+       ix_init(sc);
+       for (i = 0; i < sc->tx_ring_inuse; ++i)
+               ifsq_devstart_sched(sc->tx_rings[i].tx_ifsq);
+}
+
+static void
+ix_sync_netisr(struct ix_softc *sc, int flags)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+
+       ifnet_serialize_all(ifp);
+       if (ifp->if_flags & IFF_RUNNING) {
+               ifp->if_flags &= ~(IFF_RUNNING | flags);
+       } else {
+               ifnet_deserialize_all(ifp);
+               return;
+       }
+       ifnet_deserialize_all(ifp);
+
+       /* Make sure that polling stopped. */
+       netmsg_service_sync();
+}
+
+static void
+ix_watchdog_task(void *xsc, int pending __unused)
+{
+       struct ix_softc *sc = xsc;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+
+       ix_sync_netisr(sc, 0);
+
+       ifnet_serialize_all(ifp);
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP)
+               ix_watchdog_reset(sc);
+       ifnet_deserialize_all(ifp);
+}
+
 static void
 ix_watchdog(struct ifaltq_subque *ifsq)
 {
        struct ix_tx_ring *txr = ifsq_get_priv(ifsq);
        struct ifnet *ifp = ifsq_get_ifp(ifsq);
        struct ix_softc *sc = ifp->if_softc;
-       int i;
 
        KKASSERT(txr->tx_ifsq == ifsq);
        ASSERT_IFNET_SERIALIZED_ALL(ifp);
@@ -3944,9 +4002,11 @@ ix_watchdog(struct ifaltq_subque *ifsq)
        if_printf(ifp, "TX(%d) desc avail = %d, next TX to Clean = %d\n",
            txr->tx_idx, txr->tx_avail, txr->tx_next_clean);
 
-       ix_init(sc);
-       for (i = 0; i < sc->tx_ring_inuse; ++i)
-               ifsq_devstart_sched(sc->tx_rings[i].tx_ifsq);
+       if ((ifp->if_flags & (IFF_IDIRECT | IFF_NPOLLING | IFF_RUNNING)) ==
+           (IFF_IDIRECT | IFF_NPOLLING | IFF_RUNNING))
+               taskqueue_enqueue(taskqueue_thread[0], &sc->wdog_task);
+       else
+               ix_watchdog_reset(sc);
 }
 
 static void
@@ -4077,6 +4137,9 @@ ix_add_sysctl(struct ix_softc *sc)
            OID_AUTO, "tx_intr_nsegs", CTLTYPE_INT | CTLFLAG_RW,
            sc, 0, ix_sysctl_tx_intr_nsegs, "I",
            "# of segments per TX interrupt");
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree),
+           OID_AUTO, "direct_input", CTLFLAG_RW, &sc->direct_input, 0,
+           "Enable direct input");
        if (sc->intr_type == PCI_INTR_TYPE_MSIX) {
                SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree),
                    OID_AUTO, "tx_msix_cpumap", CTLTYPE_OPAQUE | CTLFLAG_RD,
@@ -4776,7 +4839,15 @@ ix_npoll_rx(struct ifnet *ifp __unused, void *arg, int cycle)
        struct ix_rx_ring *rxr = arg;
 
        ASSERT_SERIALIZED(&rxr->rx_serialize);
+       ix_rxeof(rxr, cycle);
+}
+
+static void
+ix_npoll_rx_direct(struct ifnet *ifp __unused, void *arg, int cycle)
+{
+       struct ix_rx_ring *rxr = arg;
 
+       ASSERT_NOT_SERIALIZED(&rxr->rx_serialize);
        ix_rxeof(rxr, cycle);
 }
 
@@ -4784,10 +4855,13 @@ static void
 ix_npoll(struct ifnet *ifp, struct ifpoll_info *info)
 {
        struct ix_softc *sc = ifp->if_softc;
-       int i, txr_cnt, rxr_cnt;
+       int i, txr_cnt, rxr_cnt, idirect;
 
        ASSERT_IFNET_SERIALIZED_ALL(ifp);
 
+       idirect = sc->direct_input;
+       cpu_ccfence();
+
        if (info) {
                int cpu;
 
@@ -4812,11 +4886,21 @@ ix_npoll(struct ifnet *ifp, struct ifpoll_info *info)
 
                        cpu = if_ringmap_cpumap(sc->rx_rmap, i);
                        KKASSERT(cpu < netisr_ncpus);
-                       info->ifpi_rx[cpu].poll_func = ix_npoll_rx;
                        info->ifpi_rx[cpu].arg = rxr;
-                       info->ifpi_rx[cpu].serializer = &rxr->rx_serialize;
+                       if (idirect) {
+                               info->ifpi_rx[cpu].poll_func =
+                                   ix_npoll_rx_direct;
+                               info->ifpi_rx[cpu].serializer = NULL;
+                       } else {
+                               info->ifpi_rx[cpu].poll_func = ix_npoll_rx;
+                               info->ifpi_rx[cpu].serializer =
+                                   &rxr->rx_serialize;
+                       }
                }
+               if (idirect)
+                       ifp->if_flags |= IFF_IDIRECT;
        } else {
+               ifp->if_flags &= ~IFF_IDIRECT;
                for (i = 0; i < sc->tx_ring_cnt; ++i) {
                        struct ix_tx_ring *txr = &sc->tx_rings[i];
 
index 085877b..60a5083 100644 (file)
@@ -358,6 +358,8 @@ struct ix_softc {
 
        int                     rdr_table[IX_RDRTABLE_SIZE];
 
+       struct task             wdog_task;
+       int                     direct_input;
 #ifdef IX_RSS_DEBUG
        int                     rss_debug;
 #endif
index 32855da..124a60e 100644 (file)
@@ -120,12 +120,13 @@ struct if_data {
 #define        IFF_MONITOR     0x40000         /* user-requested monitor mode */
 #define        IFF_STATICARP   0x80000         /* static ARP */
 #define        IFF_NPOLLING    0x100000        /* interface is in polling mode */
+#define        IFF_IDIRECT     0x200000        /* direct input */
 
 /* flags set internally only: */
 #define        IFF_CANTCHANGE \
        (IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE_COMPAT|\
         IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_SMART|IFF_POLLING_COMPAT|\
-        IFF_NPOLLING)
+        IFF_NPOLLING|IFF_IDIRECT)
 
 #ifndef _KERNEL
 /*
index 8c5a758..313ef2e 100644 (file)
@@ -104,7 +104,7 @@ static int ether_output(struct ifnet *, struct mbuf *, struct sockaddr *,
 static void ether_restore_header(struct mbuf **, const struct ether_header *,
                                 const struct ether_header *);
 static int ether_characterize(struct mbuf **);
-static void ether_dispatch(int, struct mbuf *, int);
+static void ether_dispatch(struct ifnet *, int, struct mbuf *, int);
 
 /*
  * if_bridge support
@@ -1349,6 +1349,8 @@ ether_input_handler(netmsg_t nmsg)
                        return;
                }
        }
+
+       ifp = m->m_pkthdr.rcvif;
        if ((m->m_flags & (M_HASH | M_CKHASH)) == (M_HASH | M_CKHASH) ||
            __predict_false(ether_input_ckhash)) {
                int isr;
@@ -1367,13 +1369,12 @@ ether_input_handler(netmsg_t nmsg)
                        /*
                         * Wrong hardware supplied hash; redispatch
                         */
-                       ether_dispatch(isr, m, -1);
+                       ether_dispatch(ifp, isr, m, -1);
                        if (__predict_false(ether_input_ckhash))
                                atomic_add_long(&ether_input_wronghwhash, 1);
                        return;
                }
        }
-       ifp = m->m_pkthdr.rcvif;
 
        eh = mtod(m, struct ether_header *);
        if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
@@ -1395,7 +1396,7 @@ ether_input_handler(netmsg_t nmsg)
  * so we know which netisr to send it to.
  */
 static void
-ether_dispatch(int isr, struct mbuf *m, int cpuid)
+ether_dispatch(struct ifnet *ifp, int isr, struct mbuf *m, int cpuid)
 {
        struct netmsg_packet *pmsg;
        int target_cpuid;
@@ -1411,8 +1412,12 @@ ether_dispatch(int isr, struct mbuf *m, int cpuid)
 
        logether(disp_beg, NULL);
        if (target_cpuid == cpuid) {
-               lwkt_sendmsg_oncpu(netisr_cpuport(target_cpuid),
-                   &pmsg->base.lmsg);
+               if ((ifp->if_flags & IFF_IDIRECT) && IN_NETISR_NCPUS(cpuid)) {
+                       ether_input_handler((netmsg_t)pmsg);
+               } else {
+                       lwkt_sendmsg_oncpu(netisr_cpuport(target_cpuid),
+                           &pmsg->base.lmsg);
+               }
        } else {
                lwkt_sendmsg(netisr_cpuport(target_cpuid),
                    &pmsg->base.lmsg);
@@ -1486,7 +1491,7 @@ ether_input(struct ifnet *ifp, struct mbuf *m, const struct pktinfo *pi,
 #endif
                netisr_hashcheck(pi->pi_netisr, m, pi);
                if (m->m_flags & M_HASH) {
-                       ether_dispatch(pi->pi_netisr, m, cpuid);
+                       ether_dispatch(ifp, pi->pi_netisr, m, cpuid);
 #ifdef RSS_DEBUG
                        atomic_add_long(&ether_pktinfo_hit, 1);
 #endif
@@ -1526,7 +1531,7 @@ ether_input(struct ifnet *ifp, struct mbuf *m, const struct pktinfo *pi,
        /*
         * Finally dispatch it
         */
-       ether_dispatch(isr, m, cpuid);
+       ether_dispatch(ifp, isr, m, cpuid);
 
        logether(pkt_end, ifp);
 }
index 887f28f..ada9bdc 100644 (file)
@@ -316,9 +316,19 @@ sched_iopoll(struct iopoll_ctx *io_ctx)
 }
 
 static __inline void
-sched_iopollmore(struct iopoll_ctx *io_ctx)
+sched_iopollmore(struct iopoll_ctx *io_ctx, boolean_t direct)
 {
-       ifpoll_sendmsg_oncpu((netmsg_t)&io_ctx->poll_more_netmsg);
+
+       if (!direct) {
+               ifpoll_sendmsg_oncpu((netmsg_t)&io_ctx->poll_more_netmsg);
+       } else {
+               struct netmsg_base *nmsg = &io_ctx->poll_more_netmsg;
+
+               nmsg->lmsg.ms_flags &= ~(MSGF_REPLY | MSGF_DONE);
+               nmsg->lmsg.ms_flags |= MSGF_SYNC;
+               nmsg->nm_dispatch((netmsg_t)nmsg);
+               KKASSERT(nmsg->lmsg.ms_flags & MSGF_DONE);
+       }
 }
 
 /*
@@ -804,6 +814,7 @@ rxpoll_handler(netmsg_t msg)
 {
        struct iopoll_ctx *io_ctx;
        struct thread *td = curthread;
+       boolean_t direct = TRUE;
        int i, cycles;
 
        logpoll(rx_start);
@@ -836,14 +847,26 @@ rxpoll_handler(netmsg_t msg)
                const struct iopoll_rec *rec = &io_ctx->pr[i];
                struct ifnet *ifp = rec->ifp;
 
-               if (!lwkt_serialize_try(rec->serializer))
+               if (rec->serializer != NULL &&
+                   !lwkt_serialize_try(rec->serializer))
                        continue;
 
-               if ((ifp->if_flags & (IFF_RUNNING | IFF_NPOLLING)) ==
-                   (IFF_RUNNING | IFF_NPOLLING))
+               if ((ifp->if_flags & IFF_IDIRECT) == 0) {
+                       direct = FALSE;
+               }
+#ifdef INVARIANTS
+               else {
+                       KASSERT(rec->serializer == NULL,
+                           ("serialized direct input"));
+               }
+#endif
+
+               if ((ifp->if_flags & (IFF_UP | IFF_RUNNING | IFF_NPOLLING)) ==
+                   (IFF_UP | IFF_RUNNING | IFF_NPOLLING))
                        rec->poll_func(ifp, rec->arg, cycles);
 
-               lwkt_serialize_exit(rec->serializer);
+               if (rec->serializer != NULL)
+                       lwkt_serialize_exit(rec->serializer);
        }
 
        /*
@@ -853,8 +876,8 @@ rxpoll_handler(netmsg_t msg)
        crit_exit_quick(td);
        crit_enter_quick(td);
 
-       sched_iopollmore(io_ctx);
        io_ctx->phase = 4;
+       sched_iopollmore(io_ctx, direct);
 
        crit_exit_quick(td);
 
@@ -893,8 +916,8 @@ txpoll_handler(netmsg_t msg)
                if (!lwkt_serialize_try(rec->serializer))
                        continue;
 
-               if ((ifp->if_flags & (IFF_RUNNING | IFF_NPOLLING)) ==
-                   (IFF_RUNNING | IFF_NPOLLING))
+               if ((ifp->if_flags & (IFF_UP | IFF_RUNNING | IFF_NPOLLING)) ==
+                   (IFF_UP | IFF_RUNNING | IFF_NPOLLING))
                        rec->poll_func(ifp, rec->arg, -1);
 
                lwkt_serialize_exit(rec->serializer);
@@ -907,8 +930,8 @@ txpoll_handler(netmsg_t msg)
        crit_exit_quick(td);
        crit_enter_quick(td);
 
-       sched_iopollmore(io_ctx);
        io_ctx->phase = 4;
+       sched_iopollmore(io_ctx, TRUE);
 
        crit_exit_quick(td);