diff --git a/sys/net/if.c b/sys/net/if.c index 89af5f0..684e9ba 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -2340,29 +2340,6 @@ bad: #if defined(INET) || defined(INET6) -static int -sysctl_pktq_maxlen(SYSCTLFN_ARGS, pktqueue_t *pq) -{ - u_int nmaxlen = pktq_get_count(pq, PKTQ_MAXLEN); - struct sysctlnode node = *rnode; - int error; - - node.sysctl_data = &nmaxlen; - error = sysctl_lookup(SYSCTLFN_CALL(&node)); - if (error || newp == NULL) - return error; - return pktq_set_maxlen(pq, nmaxlen); -} - -static int -sysctl_pktq_count(SYSCTLFN_ARGS, pktqueue_t *pq, u_int count_id) -{ - int count = pktq_get_count(pq, count_id); - struct sysctlnode node = *rnode; - node.sysctl_data = &count; - return sysctl_lookup(SYSCTLFN_CALL(&node)); -} - #define SYSCTL_NET_PKTQ(q, cn, c) \ static int \ sysctl_net_##q##_##cn(SYSCTLFN_ARGS) \ diff --git a/sys/net/if.h b/sys/net/if.h index 67e78d4..fffbbad 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -79,8 +79,10 @@ #include #include + #include #include +#include /* * Always include ALTQ glue here -- we use the ALTQ interface queue diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index ea7b4fc..b813019 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -100,12 +100,14 @@ __KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.77 2013/06/29 21:06:58 rmind Exp $") #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -252,6 +254,9 @@ static int bridge_ip6_checkbasic(struct mbuf **mp); # endif /* INET6 */ #endif /* BRIDGE_IPF */ +static void bridge_sysctl_fwdq_setup(struct sysctllog **clog, + struct bridge_softc *sc); + struct bridge_control { int (*bc_func)(struct bridge_softc *, void *); int bc_argsize; @@ -351,13 +356,6 @@ bridge_clone_create(struct if_clone *ifc, int unit) sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME; sc->sc_filter_flags = 0; - /* software interrupt to do the work */ - sc->sc_softintr = softint_establish(SOFTINT_NET, bridge_forward, sc); - if (sc->sc_softintr == NULL) { - free(sc, M_DEVBUF); - return ENOMEM; - } - /* Initialize our routing table. */ bridge_rtable_init(sc); @@ -380,6 +378,11 @@ bridge_clone_create(struct if_clone *ifc, int unit) ifp->if_hdrlen = ETHER_HDR_LEN; IFQ_SET_READY(&ifp->if_snd); + sc->sc_fwd_pktq = pktq_create(IFQ_MAXLEN, bridge_forward, sc); + KASSERT(sc->sc_fwd_pktq != NULL); + + bridge_sysctl_fwdq_setup(&ifp->if_sysctl_log, sc); + if_attach(ifp); if_alloc_sadl(ifp); @@ -402,6 +405,12 @@ bridge_clone_destroy(struct ifnet *ifp) struct bridge_softc *sc = ifp->if_softc; struct bridge_iflist *bif; int s; + uint64_t xc; + + /* Must be called during IFF_RUNNING, i.e., before bridge_stop */ + pktq_barrier(sc->sc_fwd_pktq); + xc = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); + xc_wait(xc); s = splnet(); @@ -416,16 +425,104 @@ bridge_clone_destroy(struct ifnet *ifp) if_detach(ifp); + /* Should be called after if_detach for safe */ + pktq_flush(sc->sc_fwd_pktq); + pktq_destroy(sc->sc_fwd_pktq); + /* Tear down the routing table. */ bridge_rtable_fini(sc); - softint_disestablish(sc->sc_softintr); - free(sc, M_DEVBUF); return (0); } +static int +bridge_sysctl_fwdq_maxlen(SYSCTLFN_ARGS) +{ + struct sysctlnode node = *rnode; + const struct bridge_softc *sc = node.sysctl_data; + return sysctl_pktq_maxlen(SYSCTLFN_CALL(rnode), sc->sc_fwd_pktq); +} + +#define SYSCTL_BRIDGE_PKTQ(cn, c) \ + static int \ + bridge_sysctl_fwdq_##cn(SYSCTLFN_ARGS) \ + { \ + struct sysctlnode node = *rnode; \ + const struct bridge_softc *sc = node.sysctl_data; \ + return sysctl_pktq_count(SYSCTLFN_CALL(rnode), \ + sc->sc_fwd_pktq, c); \ + } + +SYSCTL_BRIDGE_PKTQ(items, PKTQ_NITEMS) +SYSCTL_BRIDGE_PKTQ(drops, PKTQ_DROPS) + +static void +bridge_sysctl_fwdq_setup(struct sysctllog **clog, struct bridge_softc *sc) +{ + const struct sysctlnode *cnode, *rnode; + sysctlfn len_func = NULL, maxlen_func = NULL, drops_func = NULL; + const char *ifname = sc->sc_if.if_xname; + + len_func = bridge_sysctl_fwdq_items; + maxlen_func = bridge_sysctl_fwdq_maxlen; + drops_func = bridge_sysctl_fwdq_drops; + + if (sysctl_createv(clog, 0, NULL, &rnode, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "interfaces", + SYSCTL_DESCR("Per-interface controls"), + NULL, 0, NULL, 0, + CTL_NET, CTL_CREATE, CTL_EOL) != 0) + goto bad; + + if (sysctl_createv(clog, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, ifname, + SYSCTL_DESCR("Interface controls"), + NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL) != 0) + goto bad; + + if (sysctl_createv(clog, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "fwdq", + SYSCTL_DESCR("Protocol input queue controls"), + NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL) != 0) + goto bad; + + if (sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_INT, "len", + SYSCTL_DESCR("Current forwarding queue length"), + len_func, 0, (void *)sc, 0, + CTL_CREATE, IFQCTL_LEN, CTL_EOL) != 0) + goto bad; + + if (sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "maxlen", + SYSCTL_DESCR("Maximum allowed forwarding queue length"), + maxlen_func, 0, (void *)sc, 0, + CTL_CREATE, IFQCTL_MAXLEN, CTL_EOL) != 0) + goto bad; + + if (sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_INT, "drops", + SYSCTL_DESCR("Packets dropped due to full forwarding queue"), + drops_func, 0, (void *)sc, 0, + CTL_CREATE, IFQCTL_DROPS, CTL_EOL) != 0) + goto bad; + + return; +bad: + aprint_error("%s: could not attach sysctl nodes\n", ifname); + return; +} + /* * bridge_ioctl: * @@ -1340,19 +1437,16 @@ bridge_forward(void *v) struct ether_header *eh; int s; + KERNEL_LOCK(1, NULL); mutex_enter(softnet_lock); if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { mutex_exit(softnet_lock); + KERNEL_UNLOCK_ONE(NULL); return; } s = splnet(); - while (1) { - IFQ_POLL(&sc->sc_if.if_snd, m); - if (m == NULL) - break; - IFQ_DEQUEUE(&sc->sc_if.if_snd, m); - + while ((m = pktq_dequeue(sc->sc_fwd_pktq)) != NULL) { src_if = m->m_pkthdr.rcvif; sc->sc_if.if_ipackets++; @@ -1466,6 +1560,7 @@ bridge_forward(void *v) } splx(s); mutex_exit(softnet_lock); + KERNEL_UNLOCK_ONE(NULL); } /* @@ -1515,17 +1610,15 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) * for bridge processing; return the original packet for * local processing. */ - if (IF_QFULL(&sc->sc_if.if_snd)) { - IF_DROP(&sc->sc_if.if_snd); - return (m); - } mc = m_dup(m, 0, M_COPYALL, M_NOWAIT); if (mc == NULL) - return (m); + return m; /* Perform the bridge forwarding function with the copy. */ - IF_ENQUEUE(&sc->sc_if.if_snd, mc); - softint_schedule(sc->sc_softintr); + if (__predict_false(!pktq_enqueue(sc->sc_fwd_pktq, mc, 0))) { + m_freem(mc); + return m; + } /* Return the original packet for local processing. */ return (m); @@ -1573,13 +1666,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) } /* Perform the bridge forwarding function. */ - if (IF_QFULL(&sc->sc_if.if_snd)) { - IF_DROP(&sc->sc_if.if_snd); + if (__predict_false(!pktq_enqueue(sc->sc_fwd_pktq, m, 0))) m_freem(m); - return (NULL); - } - IF_ENQUEUE(&sc->sc_if.if_snd, m); - softint_schedule(sc->sc_softintr); return (NULL); } diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index b84d0b8..eb5ed14 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -78,6 +78,8 @@ #include #include +#include + /* * Commands used in the SIOCSDRVSPEC ioctl. Note the lookup of the * bridge interface itself is keyed off the ifdrv structure. @@ -302,6 +304,7 @@ struct bridge_softc { uint32_t sc_rthash_key; /* key for hash */ uint32_t sc_filter_flags; /* ipf and flags */ void *sc_softintr; + pktqueue_t * sc_fwd_pktq; }; extern const uint8_t bstp_etheraddr[]; diff --git a/sys/net/pktqueue.c b/sys/net/pktqueue.c index 21ae908..ce483cc 100644 --- a/sys/net/pktqueue.c +++ b/sys/net/pktqueue.c @@ -96,7 +96,7 @@ typedef struct { roundup2(offsetof(pktqueue_t, pq_queue[ncpu]), coherency_unit) pktqueue_t * -pktq_create(size_t maxlen, void (*intrh)(void *)) +pktq_create(size_t maxlen, void (*intrh)(void *), void *sc) { const u_int sflags = SOFTINT_NET | SOFTINT_MPSAFE | SOFTINT_RCPU; const size_t len = PKTQUEUE_STRUCT_LEN(ncpu); @@ -107,7 +107,7 @@ pktq_create(size_t maxlen, void (*intrh)(void *)) if ((pc = percpu_alloc(sizeof(pktq_counters_t))) == NULL) { return NULL; } - if ((sih = softint_establish(sflags, intrh, NULL)) == NULL) { + if ((sih = softint_establish(sflags, intrh, sc)) == NULL) { percpu_free(pc, sizeof(pktq_counters_t)); return NULL; } @@ -361,3 +361,26 @@ pktq_set_maxlen(pktqueue_t *pq, size_t maxlen) kmem_free(qs, slotbytes); return 0; } + +int +sysctl_pktq_maxlen(SYSCTLFN_ARGS, pktqueue_t *pq) +{ + u_int nmaxlen = pktq_get_count(pq, PKTQ_MAXLEN); + struct sysctlnode node = *rnode; + int error; + + node.sysctl_data = &nmaxlen; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + return pktq_set_maxlen(pq, nmaxlen); +} + +int +sysctl_pktq_count(SYSCTLFN_ARGS, pktqueue_t *pq, u_int count_id) +{ + int count = pktq_get_count(pq, count_id); + struct sysctlnode node = *rnode; + node.sysctl_data = &count; + return sysctl_lookup(SYSCTLFN_CALL(&node)); +} diff --git a/sys/net/pktqueue.h b/sys/net/pktqueue.h index f246115..b6a52d8 100644 --- a/sys/net/pktqueue.h +++ b/sys/net/pktqueue.h @@ -36,13 +36,15 @@ #error "not supposed to be exposed to userland." #endif +#include + struct mbuf; typedef struct pktqueue pktqueue_t; typedef enum { PKTQ_MAXLEN, PKTQ_NITEMS, PKTQ_DROPS } pktq_count_t; -pktqueue_t * pktq_create(size_t, void (*)(void *)); +pktqueue_t * pktq_create(size_t, void (*)(void *), void *); void pktq_destroy(pktqueue_t *); bool pktq_enqueue(pktqueue_t *, struct mbuf *, const u_int); @@ -54,4 +56,7 @@ int pktq_set_maxlen(pktqueue_t *, size_t); uint32_t pktq_rps_hash(const struct mbuf *); uint64_t pktq_get_count(pktqueue_t *, pktq_count_t); +int sysctl_pktq_maxlen(SYSCTLFN_PROTO, pktqueue_t *); +int sysctl_pktq_count(SYSCTLFN_PROTO, pktqueue_t *, u_int); + #endif diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index ece1c5d..a5de132 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -304,7 +304,7 @@ ip_init(void) pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); KASSERT(pr != NULL); - ip_pktq = pktq_create(IFQ_MAXLEN, ipintr); + ip_pktq = pktq_create(IFQ_MAXLEN, ipintr, NULL); KASSERT(ip_pktq != NULL); for (u_int i = 0; i < IPPROTO_MAX; i++) { diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 020ab90..9b3bac3 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -180,7 +180,7 @@ ip6_init(void) pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) ip6_protox[pr->pr_protocol] = pr - inet6sw; - ip6_pktq = pktq_create(IFQ_MAXLEN, ip6intr); + ip6_pktq = pktq_create(IFQ_MAXLEN, ip6intr, NULL); KASSERT(ip6_pktq != NULL); scope6_init();