Module Name: src Committed By: martin Date: Thu Oct 3 11:53:46 UTC 2024
Modified Files: src/sys/net/lagg [netbsd-10]: if_lagg.c if_lagg_lacp.c if_lagg_lacp.h if_laggproto.c if_laggproto.h src/tests/net/if_lagg [netbsd-10]: t_lagg.sh Log Message: Pull up following revision(s) (requested by rin in ticket #916): sys/net/lagg/if_laggproto.c: revision 1.15 sys/net/lagg/if_lagg_lacp.c: revision 1.36 sys/net/lagg/if_laggproto.c: revision 1.16 sys/net/lagg/if_lagg_lacp.c: revision 1.37 sys/net/lagg/if_lagg_lacp.c: revision 1.38 sys/net/lagg/if_lagg_lacp.c: revision 1.39 sys/net/lagg/if_lagg.c: revision 1.54 sys/net/lagg/if_lagg.c: revision 1.55 sys/net/lagg/if_lagg.c: revision 1.59 sys/net/lagg/if_lagg.c: revision 1.70 sys/net/lagg/if_laggproto.h: revision 1.19 sys/net/lagg/if_lagg_lacp.c: revision 1.28 sys/net/lagg/if_lagg_lacp.c: revision 1.29 sys/net/lagg/if_laggproto.c: revision 1.7 sys/net/lagg/if_lagg_lacp.h: revision 1.5 sys/net/lagg/if_laggproto.c: revision 1.8 sys/net/lagg/if_laggproto.c: revision 1.9 sys/net/lagg/if_lagg_lacp.c: revision 1.40 sys/net/lagg/if_lagg_lacp.c: revision 1.41 sys/net/lagg/if_lagg_lacp.c: revision 1.42 sys/net/lagg/if_lagg_lacp.c: revision 1.43 tests/net/if_lagg/t_lagg.sh: revision 1.11 sys/net/lagg/if_lagg.c: revision 1.60 sys/net/lagg/if_lagg.c: revision 1.62 sys/net/lagg/if_lagg.c: revision 1.63 sys/net/lagg/if_lagg.c: revision 1.64 sys/net/lagg/if_laggproto.h: revision 1.20 sys/net/lagg/if_lagg.c: revision 1.65 sys/net/lagg/if_lagg.c: revision 1.66 sys/net/lagg/if_lagg.c: revision 1.67 sys/net/lagg/if_lagg_lacp.c: revision 1.30 sys/net/lagg/if_lagg.c: revision 1.68 sys/net/lagg/if_laggproto.c: revision 1.10 sys/net/lagg/if_lagg_lacp.c: revision 1.31 sys/net/lagg/if_lagg.c: revision 1.69 sys/net/lagg/if_laggproto.c: revision 1.11 sys/net/lagg/if_lagg_lacp.c: revision 1.32 sys/net/lagg/if_laggproto.c: revision 1.12 sys/net/lagg/if_lagg_lacp.c: revision 1.33 sys/net/lagg/if_laggproto.c: revision 1.13 sys/net/lagg/if_lagg_lacp.c: revision 1.34 sys/net/lagg/if_laggproto.c: revision 1.14 sys/net/lagg/if_lagg_lacp.c: revision 1.35 Set the fastest linkspeed in each physical interface to lagg(4) lagg(4): Added logs about LACP processing lagg(4): Fix missing IFNET_LOCK acquirement lagg(4): update link speed when a physical interface is removed lagg(4): fix missing update of the number of active ports lagg(4): Added 0 length check lagg(4): Added LACP_READY state for logging when a port turns SELECTED or UNSELECTED lagg(4): added log on detaching a port from SELECTED state to STANDBY acquire LAGG_PROTO_LOCK instead of pserialize read section lagg(4): Remove unnecessary LAGG_LOCK holding while lagg_proto_detach() to avoid deadlock in workqueue_wait due to LAGG_LOCK holding lagg_proto_detach dose not need to hold LAGG_LOCK because only one context can access to a detaching protocol after sc->sc_var is updated. But it was held without any reason. And it had caused a deadlock by holding LAGG_LOCK in caller of workqueue_wait and waiting for the lock in worker. added missing LAGG_UNLOCK() lagg(4): move comment about IFF_PROMISC pointed out by ozaki-r@, thanks. lagg(4): added NULL check for pfil_run_hooks pointed out by ozaki-r@, thanks. lagg(4): change errno suggested by ozaki-r@, thanks. lagg(4): increase output packets and bytes only if no error occurred pointed out by ozaki-r@, thanks. lagg(4): replace NULL check with KASSERT because lp_softc is always non-NULL lagg(4): Use CTASSERT Added KASSERT for LACP_LOCK lagg(4): move allocate memory before ioctl Added comments to lagg(4) lagg(4): added __predict_true lagg(4): added missing pserialize_read_enter fix missing LACP_LOCK lagg(4): added check of LACP running state for safety When LACP stops, the handler of callout do nothing because all port is already detached from lacp. Therefore, the added checks are just for safety. added missing workq_wait for lacp_tick_work() lagg(4): set suppress at the same time with distribution state lagg(4): remove unnecessary masking pointed out by ozaki-r@, thanks. lagg(4): move reply limitation to recive processing lagg(4): release lock before pserialize_perform() if possible lagg(4): Added vlan check lagg(4): Fix missing destroy for list and entry lagg(4) test: Fix typo and old comment lagg: fill name of workqueue correctly Found by KASSERT failure for DIAGNOSTIC kernel. Authored by ozaki-r@. To generate a diff of this commit: cvs rdiff -u -r1.48.4.3 -r1.48.4.4 src/sys/net/lagg/if_lagg.c cvs rdiff -u -r1.25.4.1 -r1.25.4.2 src/sys/net/lagg/if_lagg_lacp.c cvs rdiff -u -r1.4 -r1.4.4.1 src/sys/net/lagg/if_lagg_lacp.h cvs rdiff -u -r1.6 -r1.6.4.1 src/sys/net/lagg/if_laggproto.c cvs rdiff -u -r1.18 -r1.18.4.1 src/sys/net/lagg/if_laggproto.h cvs rdiff -u -r1.8.2.1 -r1.8.2.2 src/tests/net/if_lagg/t_lagg.sh Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/lagg/if_lagg.c diff -u src/sys/net/lagg/if_lagg.c:1.48.4.3 src/sys/net/lagg/if_lagg.c:1.48.4.4 --- src/sys/net/lagg/if_lagg.c:1.48.4.3 Tue Dec 12 16:45:00 2023 +++ src/sys/net/lagg/if_lagg.c Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: if_lagg.c,v 1.48.4.3 2023/12/12 16:45:00 martin Exp $ */ +/* $NetBSD: if_lagg.c,v 1.48.4.4 2024/10/03 11:53:46 martin Exp $ */ /* * Copyright (c) 2005, 2006 Reyk Floeter <r...@openbsd.org> @@ -20,7 +20,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_lagg.c,v 1.48.4.3 2023/12/12 16:45:00 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_lagg.c,v 1.48.4.4 2024/10/03 11:53:46 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_inet.h" @@ -114,7 +114,7 @@ static const struct lagg_proto lagg_prot .pr_startport = lagg_common_startport, .pr_stopport = lagg_common_stopport, .pr_portstat = lagg_fail_portstat, - .pr_linkstate = lagg_common_linkstate, + .pr_linkstate = lagg_common_linkstate_ifnet_locked, .pr_ioctl = lagg_fail_ioctl, }, [LAGG_PROTO_LOADBALANCE] = { @@ -128,7 +128,7 @@ static const struct lagg_proto lagg_prot .pr_startport = lagg_lb_startport, .pr_stopport = lagg_lb_stopport, .pr_portstat = lagg_lb_portstat, - .pr_linkstate = lagg_common_linkstate, + .pr_linkstate = lagg_common_linkstate_ifnet_locked, }, }; @@ -1024,7 +1024,7 @@ lagg_tx_common(struct ifnet *ifp, struct } else { m_freem(m); if_statinc(ifp, if_oerrors); - error = ENOBUFS; + error = EIO; } lagg_variant_putref(var, &psref); @@ -1065,22 +1065,26 @@ lagg_output(struct lagg_softc *sc, struc mflags = m->m_flags; error = pfil_run_hooks(ifp->if_pfil, &m, ifp, PFIL_OUT); - if (error != 0) + if (error != 0) { + if (m != NULL) { + m_freem(m); + } return; + } bpf_mtap(ifp, m, BPF_D_OUT); error = lagg_port_xmit(lp, m); if (error) { /* mbuf is already freed */ if_statinc(ifp, if_oerrors); + } else { + net_stat_ref_t nsr = IF_STAT_GETREF(ifp); + if_statinc_ref(nsr, if_opackets); + if_statadd_ref(nsr, if_obytes, len); + if (mflags & M_MCAST) + if_statinc_ref(nsr, if_omcasts); + IF_STAT_PUTREF(ifp); } - - net_stat_ref_t nsr = IF_STAT_GETREF(ifp); - if_statinc_ref(nsr, if_opackets); - if_statadd_ref(nsr, if_obytes, len); - if (mflags & M_MCAST) - if_statinc_ref(nsr, if_omcasts); - IF_STAT_PUTREF(ifp); } static struct mbuf * @@ -1136,11 +1140,6 @@ lagg_input_ethernet(struct ifnet *ifp_po ifp = &lp->lp_softc->sc_if; - /* - * Drop promiscuously received packets - * if we are not in promiscuous mode. - */ - if (__predict_false(m->m_len < (int)sizeof(*eh))) { if ((m = m_pullup(m, sizeof(*eh))) == NULL) { if_statinc(ifp, if_ierrors); @@ -1163,6 +1162,10 @@ lagg_input_ethernet(struct ifnet *ifp_po if_statinc(ifp_port, if_imcasts); } else { + /* + * Drop promiscuously received packets + * if we are not in promiscuous mode. + */ if ((ifp->if_flags & IFF_PROMISC) == 0 && (ifp_port->if_flags & IFF_PROMISC) != 0 && memcmp(CLLADDR(ifp->if_sadl), eh->ether_dhost, @@ -1173,8 +1176,13 @@ lagg_input_ethernet(struct ifnet *ifp_po if_statadd(ifp_port, if_ibytes, m->m_pkthdr.len); if (pfil_run_hooks(ifp_port->if_pfil, &m, - ifp_port, PFIL_IN) != 0) + ifp_port, PFIL_IN) != 0) { + if (m != NULL) { + m_freem(m); + m = NULL; + } goto out; + } m = lagg_proto_input(lp->lp_softc, lp, m); if (m != NULL) { @@ -1216,6 +1224,9 @@ lagg_media_status(struct ifnet *ifp, str imr->ifm_active = IFM_ETHER | IFM_AUTO; LAGG_LOCK(sc); + + imr->ifm_active |= sc->sc_media_active; + LAGG_PORTS_FOREACH(sc, lp) { if (lagg_portactive(lp)) imr->ifm_status |= IFM_ACTIVE; @@ -1223,6 +1234,52 @@ lagg_media_status(struct ifnet *ifp, str LAGG_UNLOCK(sc); } +static uint64_t +lagg_search_media_type(uint64_t linkspeed) +{ + + if (linkspeed == IF_Gbps(40)) + return IFM_40G_T | IFM_FDX; + + if (linkspeed == IF_Gbps(25)) + return IFM_25G_T | IFM_FDX; + + if (linkspeed == IF_Gbps(10)) + return IFM_10G_T | IFM_FDX; + + if (linkspeed == IF_Gbps(5)) + return IFM_5000_T | IFM_FDX; + + if (linkspeed == IF_Mbps(2500)) + return IFM_2500_T | IFM_FDX; + + if (linkspeed == IF_Gbps(1)) + return IFM_1000_T | IFM_FDX; + + if (linkspeed == IF_Mbps(100)) + return IFM_100_TX | IFM_FDX; + + if (linkspeed == IF_Mbps(10)) + return IFM_10_T | IFM_FDX; + + return 0; +} + +void +lagg_set_linkspeed(struct lagg_softc *sc, uint64_t linkspeed) +{ + struct ifnet *ifp; + + ifp = &sc->sc_if; + + KASSERT(LAGG_LOCKED(sc)); + + ifp->if_baudrate = linkspeed; + + sc->sc_media_active = + lagg_search_media_type(linkspeed); +} + static int lagg_port_vlan_cb(struct lagg_port *lp, struct lagg_vlantag *lvt, bool set) @@ -1600,11 +1657,9 @@ lagg_pr_attach(struct lagg_softc *sc, la { struct lagg_variant *newvar, *oldvar; struct lagg_proto_softc *psc; - bool cleanup_oldvar; int error; error = 0; - cleanup_oldvar = false; newvar = kmem_alloc(sizeof(*newvar), KM_SLEEP); LAGG_LOCK(sc); @@ -1612,30 +1667,29 @@ lagg_pr_attach(struct lagg_softc *sc, la if (oldvar != NULL && oldvar->lv_proto == pr) { error = 0; - goto done; + goto failed; } error = lagg_proto_attach(sc, pr, &psc); if (error != 0) - goto done; + goto failed; newvar->lv_proto = pr; newvar->lv_psc = psc; - lagg_variant_update(sc, newvar); - newvar = NULL; + lagg_set_linkspeed(sc, 0); + LAGG_UNLOCK(sc); if (oldvar != NULL) { lagg_proto_detach(oldvar); - cleanup_oldvar = true; + kmem_free(oldvar, sizeof(*oldvar)); } -done: - LAGG_UNLOCK(sc); - if (newvar != NULL) - kmem_free(newvar, sizeof(*newvar)); - if (cleanup_oldvar) - kmem_free(oldvar, sizeof(*oldvar)); + return 0; + +failed: + LAGG_UNLOCK(sc); + kmem_free(newvar, sizeof(*newvar)); return error; } @@ -1646,15 +1700,14 @@ lagg_pr_detach(struct lagg_softc *sc) struct lagg_variant *var; LAGG_LOCK(sc); - var = sc->sc_var; atomic_store_release(&sc->sc_var, NULL); + LAGG_UNLOCK(sc); pserialize_perform(sc->sc_psz); if (var != NULL) lagg_proto_detach(var); - LAGG_UNLOCK(sc); if (var != NULL) kmem_free(var, sizeof(*var)); @@ -2109,6 +2162,7 @@ lagg_port_setsadl(struct lagg_port *lp, if (ifp_port->if_init != NULL) { error = 0; + /* Apply updated ifp_port->if_sadl to the device */ if (ISSET(ifp_port->if_flags, IFF_RUNNING)) error = if_init(ifp_port); @@ -2210,6 +2264,9 @@ lagg_port_setup(struct lagg_softc *sc, switch (ifp_port->if_type) { case IFT_ETHER: case IFT_L2TP: + if (VLAN_ATTACHED((struct ethercom *)ifp_port)) + return EBUSY; + if_type = IFT_IEEE8023ADLAG; break; default: @@ -2655,10 +2712,11 @@ lagg_port_ioctl(struct ifnet *ifp, u_lon int error = 0; u_int ifflags; - if ((lp = ifp->if_lagg) == NULL || - (sc = lp->lp_softc) == NULL) { + if ((lp = ifp->if_lagg) == NULL) goto fallback; - } + + sc = lp->lp_softc; + KASSERT(sc != NULL); KASSERT(IFNET_LOCKED(lp->lp_ifp)); @@ -2730,12 +2788,9 @@ lagg_ifdetach(void *xifp_port) if (lp == NULL) { pserialize_read_exit(s); return; - } - - sc = lp->lp_softc; - if (sc == NULL) { - pserialize_read_exit(s); - return; + } else { + sc = lp->lp_softc; + KASSERT(sc != NULL); } pserialize_read_exit(s); @@ -2875,7 +2930,9 @@ lagg_chg_sadl(struct ifnet *ifp, const u error = 0; ifa_lla = NULL; + /* Renew all AF_LINK address to update sdl_type */ while (1) { + /* find a Link-Level address that has the previous sdl_type */ s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa_cur, ifp) { sdl = satocsdl(ifa_cur->ifa_addr); @@ -2891,7 +2948,10 @@ lagg_chg_sadl(struct ifnet *ifp, const u if (ifa_cur == NULL) break; - + /* + * create a new address that has new sdl_type, + * and copy address from the previous. + */ ifa_next = if_dl_create(ifp, &nsdl); if (ifa_next == NULL) { error = ENOMEM; @@ -2903,6 +2963,7 @@ lagg_chg_sadl(struct ifnet *ifp, const u CLLADDR(sdl), ifp->if_addrlen); ifa_insert(ifp, ifa_next); + /* the next Link-Level address is already set */ if (ifa_lla == NULL && memcmp(CLLADDR(sdl), lla, lla_len) == 0) { ifa_lla = ifa_next; @@ -2918,6 +2979,7 @@ lagg_chg_sadl(struct ifnet *ifp, const u ifafree(ifa_cur); } + /* remove the old address */ ifaref(ifa_cur); ifa_release(ifa_cur, &psref_cur); ifa_remove(ifp, ifa_cur); @@ -2927,6 +2989,7 @@ lagg_chg_sadl(struct ifnet *ifp, const u ifa_release(ifa_next, &psref_next); } + /* acquire or create the next Link-Level address */ if (ifa_lla != NULL) { ifa_next = ifa_lla; @@ -2946,13 +3009,19 @@ lagg_chg_sadl(struct ifnet *ifp, const u ifa_insert(ifp, ifa_next); } - if (ifa_next != ifp->if_dl) { + /* Activate the next Link-Level address */ + if (__predict_true(ifa_next != ifp->if_dl)) { + /* save the current address */ ifa_cur = ifp->if_dl; if (ifa_cur != NULL) ifa_acquire(ifa_cur, &psref_cur); if_activate_sadl(ifp, ifa_next, nsdl); + /* + * free the saved address after switching, + * if the address is not if_hwdl. + */ if (ifa_cur != NULL) { if (ifa_cur != ifp->if_hwdl) { ifaref(ifa_cur); Index: src/sys/net/lagg/if_lagg_lacp.c diff -u src/sys/net/lagg/if_lagg_lacp.c:1.25.4.1 src/sys/net/lagg/if_lagg_lacp.c:1.25.4.2 --- src/sys/net/lagg/if_lagg_lacp.c:1.25.4.1 Mon Nov 27 20:05:57 2023 +++ src/sys/net/lagg/if_lagg_lacp.c Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: if_lagg_lacp.c,v 1.25.4.1 2023/11/27 20:05:57 martin Exp $ */ +/* $NetBSD: if_lagg_lacp.c,v 1.25.4.2 2024/10/03 11:53:46 martin Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_lagg_lacp.c,v 1.25.4.1 2023/11/27 20:05:57 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_lagg_lacp.c,v 1.25.4.2 2024/10/03 11:53:46 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_lagg.h" @@ -75,6 +75,7 @@ enum { enum lacp_selected { LACP_UNSELECTED, + LACP_READY, LACP_STANDBY, LACP_SELECTED, }; @@ -126,7 +127,7 @@ struct lacp_port { struct lacp_aggregator *lp_aggregator; struct lacp_aggregator_systemid lp_aggregator_sidbuf; - uint32_t lp_media; + uint64_t lp_linkspeed; int lp_pending; LIST_ENTRY(lacp_port) lp_entry_la; struct timeval lp_last_lacpdu; @@ -165,6 +166,7 @@ struct lacp_softc { struct workqueue *lsc_workq; struct lagg_work lsc_work_tick; struct lagg_work lsc_work_rcvdu; + struct lagg_work lsc_work_linkspeed; callout_t lsc_tick; pcq_t *lsc_du_q; @@ -247,7 +249,6 @@ static void lacp_dprintf(const struct la static void lacp_tick(void *); static void lacp_tick_work(struct lagg_work *, void *); static void lacp_linkstate(struct lagg_proto_softc *, struct lagg_port *); -static uint32_t lacp_ifmedia2lacpmedia(u_int); static void lacp_port_disable(struct lacp_softc *, struct lacp_port *); static void lacp_port_enable(struct lacp_softc *, struct lacp_port *); static void lacp_peerinfo_actor(struct lacp_softc *, struct lacp_port *, @@ -285,6 +286,7 @@ static void lacp_sm_ptx_update_timeout(s static void lacp_rcvdu_work(struct lagg_work *, void *); static void lacp_marker_work(struct lagg_work *, void *); +static void lacp_linkspeed_work(struct lagg_work *, void *); static void lacp_dump_lacpdutlv(const struct lacpdu_peerinfo *, const struct lacpdu_peerinfo *, const struct lacpdu_collectorinfo *); @@ -378,7 +380,7 @@ lacp_mcastaddr(struct ifreq *ifr, const ifr->ifr_addr.sa_len = sizeof(ifr->ifr_addr); ifr->ifr_addr.sa_family = AF_UNSPEC; - KASSERT(sizeof(ifr->ifr_addr) >= sizeof(addr)); + CTASSERT(sizeof(ifr->ifr_addr) >= sizeof(addr)); memcpy(&ifr->ifr_addr.sa_data, addr, sizeof(addr)); } @@ -413,9 +415,9 @@ lacp_port_priority_max(struct lacp_port if (pri_b < pri_a) return b; - if (a->lp_media > b->lp_media) + if (a->lp_linkspeed > b->lp_linkspeed) return a; - if (b->lp_media > a->lp_media) + if (b->lp_linkspeed > a->lp_linkspeed) return b; return a; @@ -487,6 +489,8 @@ lacp_attach(struct lagg_softc *sc, struc lagg_work_set(&lsc->lsc_work_tick, lacp_tick_work, lsc); lagg_work_set(&lsc->lsc_work_rcvdu, lacp_rcvdu_work, lsc); + lagg_work_set(&lsc->lsc_work_linkspeed, + lacp_linkspeed_work, lsc); snprintf(xnamebuf, sizeof(xnamebuf), "%s.lacp", sc->sc_if.if_xname); @@ -532,13 +536,15 @@ lacp_detach(struct lagg_proto_softc *xls struct lacp_softc *lsc = (struct lacp_softc *)xlsc; struct lagg_softc *sc __diagused = lsc->lsc_softc; - KASSERT(LAGG_LOCKED(lsc->lsc_softc)); KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators)); KASSERT(SIMPLEQ_EMPTY(&sc->sc_ports)); + LAGG_LOCK(lsc->lsc_softc); lacp_down(xlsc); + LAGG_UNLOCK(lsc->lsc_softc); lagg_workq_wait(lsc->lsc_workq, &lsc->lsc_work_rcvdu); + lagg_workq_wait(lsc->lsc_workq, &lsc->lsc_work_tick); evcnt_detach(&lsc->lsc_mgethdr_failed); evcnt_detach(&lsc->lsc_mpullup_failed); evcnt_detach(&lsc->lsc_badlacpdu); @@ -635,7 +641,7 @@ lacp_transmit(struct lagg_proto_softc *x if (__predict_false(lsc->lsc_suppress_distributing)) { LACP_DPRINTF((lsc, NULL, "waiting transit\n")); m_freem(m); - return ENOBUFS; + return EBUSY; } lp = lacp_select_tx_port(lsc, m, &psref); @@ -669,6 +675,10 @@ lacp_allocport(struct lagg_proto_softc * KASSERT(LAGG_LOCKED(sc)); KASSERT(IFNET_LOCKED(lp->lp_ifp)); + lacpp = kmem_zalloc(sizeof(*lacpp), KM_NOSLEEP); + if (lacpp == NULL) + return ENOMEM; + lacp_mcastaddr(&ifr, lp->lp_ifp->if_xname); error = lp->lp_ioctl(lp->lp_ifp, SIOCADDMULTI, (void *)&ifr); @@ -682,13 +692,10 @@ lacp_allocport(struct lagg_proto_softc * default: LAGG_LOG(sc, LOG_ERR, "SIOCADDMULTI failed on %s\n", lp->lp_ifp->if_xname); + kmem_free(lacpp, sizeof(*lacpp)); return error; } - lacpp = kmem_zalloc(sizeof(*lacpp), KM_NOSLEEP); - if (lacpp == NULL) - return ENOMEM; - lacpp->lp_added_multi = added_multi; lagg_work_set(&lacpp->lp_work_smtx, lacp_sm_tx_work, lsc); lagg_work_set(&lacpp->lp_work_marker, lacp_marker_work, lsc); @@ -852,7 +859,7 @@ lacp_linkstate_ifnet_locked(struct lagg_ struct ifmediareq ifmr; struct ifnet *ifp_port; uint8_t old_state; - uint32_t media, old_media; + uint64_t old_linkspeed, new_linkspeed; int error; KASSERT(IFNET_LOCKED(lp->lp_ifp)); @@ -861,14 +868,24 @@ lacp_linkstate_ifnet_locked(struct lagg_ ifp_port = lp->lp_ifp; lacpp = lp->lp_proto_ctx; - media = LACP_MEDIA_DEFAULT; memset(&ifmr, 0, sizeof(ifmr)); ifmr.ifm_count = 0; error = if_ioctl(ifp_port, SIOCGIFMEDIA, (void *)&ifmr); if (error == 0) { - media = lacp_ifmedia2lacpmedia(ifmr.ifm_active); - } else if (error != ENOTTY){ + new_linkspeed = ifmedia_baudrate(ifmr.ifm_active); +#ifdef LACP_NOFDX + /* + * some driver that has no media, e.g. vioif(4), + * returns (IFM_ETHER | IFM_AUTO) + */ + if ((ifmr.ifm_active & ~(IFM_ETHER | IFM_AUTO)) == 0) + ifmr.ifm_active |= IFM_FDX; +#endif + } else if (error == ENOTTY) { + ifmr.ifm_active = IFM_FDX | IF_Gbps(0); + new_linkspeed = 0; + } else { LACP_DPRINTF((lsc, lacpp, "SIOCGIFMEDIA failed (%d)\n", error)); return; @@ -876,34 +893,31 @@ lacp_linkstate_ifnet_locked(struct lagg_ LACP_LOCK(lsc); if (lsc->lsc_running) { - old_media = lacpp->lp_media; + old_linkspeed = lacpp->lp_linkspeed; old_state = lacpp->lp_actor.lpi_state; - if (lacpp->lp_media != media) { + if (new_linkspeed != old_linkspeed) { LACP_DPRINTF((lsc, lacpp, - "media changed 0x%"PRIx32"->0x%"PRIx32", " - "ether = %d, fdx = %d, link = %d, running = %d\n", - lacpp->lp_media, media, - ISSET(media, LACP_MEDIA_ETHER) != 0, - ISSET(media, LACP_MEDIA_FDX) != 0, - ifp_port->if_link_state != LINK_STATE_DOWN, - ISSET(ifp_port->if_flags, IFF_RUNNING) != 0)); - lacpp->lp_media = media; + "linkspeed changed %"PRIu64" -> %"PRIu64"\n", + old_linkspeed, new_linkspeed)); + lacpp->lp_linkspeed = new_linkspeed; } - if (ISSET(media, LACP_MEDIA_ETHER) && -#ifndef LACP_NOFDX - ISSET(media, LACP_MEDIA_FDX) && -#endif - ifp_port->if_link_state != LINK_STATE_DOWN && - ISSET(ifp_port->if_flags, IFF_RUNNING)) { + if (ISSET(ifmr.ifm_active, IFM_FDX) && + ISSET(ifp_port->if_flags, IFF_RUNNING) && + ifp_port->if_link_state != LINK_STATE_DOWN) { lacp_port_enable(lsc, lacpp); } else { + LACP_DPRINTF((lsc, lacpp, + "FDX=%d, RUNNING=%d, link=%d\n", + ISSET(ifmr.ifm_active, IFM_FDX) != 0, + ISSET(ifp_port->if_flags, IFF_RUNNING) != 0, + ifp_port->if_link_state != LINK_STATE_DOWN)); lacp_port_disable(lsc, lacpp); } if (old_state != lacpp->lp_actor.lpi_state || - old_media != media) { + old_linkspeed != new_linkspeed) { LACP_DPRINTF((lsc, lacpp, "state changed to UNSELECTED\n")); lacpp->lp_selected = LACP_UNSELECTED; @@ -912,7 +926,6 @@ lacp_linkstate_ifnet_locked(struct lagg_ LACP_DPRINTF((lsc, lacpp, "LACP is inactive, skip linkstate\n")); } - LACP_UNLOCK(lsc); } @@ -1278,7 +1291,7 @@ lacp_rcvdu_work(struct lagg_work *lw __u struct lagg_port *lp; struct mbuf *m; uint8_t subtype; - int bound, s; + int bound, s0, s1; bound = curlwp_bind(); @@ -1287,23 +1300,26 @@ lacp_rcvdu_work(struct lagg_work *lw __u if (m == NULL) break; - ifp = m_get_rcvif(m, &s); + ifp = m_get_rcvif(m, &s0); if (ifp == NULL) { m_freem(m); lsc->lsc_norcvif.ev_count++; continue; } + s1 = pserialize_read_enter(); lp = atomic_load_consume(&ifp->if_lagg); if (lp == NULL) { - m_put_rcvif(ifp, &s); + pserialize_read_exit(s1); + m_put_rcvif(ifp, &s0); m_freem(m); lsc->lsc_norcvif.ev_count++; continue; } lagg_port_getref(lp, &psref_lp); - m_put_rcvif(ifp, &s); + pserialize_read_exit(s1); + m_put_rcvif(ifp, &s0); m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); @@ -1344,10 +1360,6 @@ lacp_port_need_to_tell(struct lacp_port if (!ISSET(lacpp->lp_flags, LACP_PORT_NTT)) return false; - if (ppsratecheck(&lacpp->lp_last_lacpdu, &lacpp->lp_lacpdu_sent, - (LACP_SENDDU_PPS / LACP_FAST_PERIODIC_TIME)) == 0) - return false; - return true; } @@ -1429,10 +1441,13 @@ lacp_sm_tx_work(struct lagg_work *lw, vo lsc = xlsc; lacpp = container_of(lw, struct lacp_port, lp_work_smtx); - if (lsc->lsc_stop_lacpdu) + LACP_LOCK(lsc); + + if (lsc->lsc_stop_lacpdu) { + LACP_UNLOCK(lsc); return; + } - LACP_LOCK(lsc); m = lacp_lacpdu_mbuf(lsc, lacpp); if (m == NULL) { LACP_UNLOCK(lsc); @@ -1473,10 +1488,16 @@ lacp_tick(void *xlsc) lsc = xlsc; - lagg_workq_add(lsc->lsc_workq, &lsc->lsc_work_tick); - LACP_LOCK(lsc); + + if (!lsc->lsc_running) { + LACP_UNLOCK(lsc); + return; + } + + lagg_workq_add(lsc->lsc_workq, &lsc->lsc_work_tick); callout_schedule(&lsc->lsc_tick, hz); + LACP_UNLOCK(lsc); } @@ -1485,6 +1506,8 @@ lacp_run_timers(struct lacp_softc *lsc, { size_t i; + KASSERT(LACP_LOCKED(lsc)); + for (i = 0; i < LACP_NTIMER; i++) { KASSERT(lacpp->lp_timer[i] >= 0); @@ -1528,6 +1551,11 @@ lacp_tick_work(struct lagg_work *lw __un sc = lsc->lsc_softc; LACP_LOCK(lsc); + if (!lsc->lsc_running) { + LACP_UNLOCK(lsc); + return; + } + lacp_run_prototimers(lsc); LACP_UNLOCK(lsc); @@ -1803,6 +1831,8 @@ static void lacp_port_disable(struct lacp_softc *lsc, struct lacp_port *lacpp) { + KASSERT(LACP_LOCKED(lsc)); + if (ISSET(lacpp->lp_actor.lpi_state, LACP_STATE_AGGREGATION)) LACP_DPRINTF((lsc, lacpp, "enable -> disable\n")); @@ -1818,6 +1848,8 @@ lacp_port_enable(struct lacp_softc *lsc struct lacp_port *lacpp) { + KASSERT(LACP_LOCKED(lsc)); + if (!ISSET(lacpp->lp_actor.lpi_state, LACP_STATE_AGGREGATION)) LACP_DPRINTF((lsc, lacpp, "disable -> enable\n")); @@ -1829,6 +1861,8 @@ static void lacp_sm_rx_timer(struct lacp_softc *lsc, struct lacp_port *lacpp) { + KASSERT(LACP_LOCKED(lsc)); + if (!ISSET(lacpp->lp_actor.lpi_state, LACP_STATE_EXPIRED)) { /* CURRENT -> EXPIRED */ LACP_DPRINTF((lsc, lacpp, "CURRENT -> EXPIRED\n")); @@ -1844,6 +1878,7 @@ static void lacp_sm_ptx_timer(struct lacp_softc *lsc __unused, struct lacp_port *lacpp) { + KASSERT(LACP_LOCKED(lsc)); lacp_sm_assert_ntt(lacpp); } @@ -1891,6 +1926,7 @@ lacp_sm_mux_timer(struct lacp_softc *lsc { char buf[LACP_SYSTEMIDSTR_LEN] __LACPDEBUGUSED; + KASSERT(LACP_LOCKED(lsc)); KASSERT(lacpp->lp_pending > 0); LACP_AGGREGATOR_STR(lacpp->lp_aggregator, buf, sizeof(buf)); @@ -1948,8 +1984,14 @@ lacp_sm_rx_update_ntt(struct lacp_softc if (lacp_compare_peerinfo(&actor, my_pi) != 0 || !LACP_STATE_EQ(lacpp->lp_actor.lpi_state, my_pi->lpi_state, LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { - LACP_DPRINTF((lsc, lacpp, "assert ntt\n")); - lacp_sm_assert_ntt(lacpp); + if (ppsratecheck(&lacpp->lp_last_lacpdu, &lacpp->lp_lacpdu_sent, + (LACP_SENDDU_PPS / LACP_FAST_PERIODIC_TIME)) == 0) { + LACP_DPRINTF((lsc, lacpp, + "skip ntt due to rate limit")); + } else { + LACP_DPRINTF((lsc, lacpp, "assert ntt\n")); + lacp_sm_assert_ntt(lacpp); + } } } @@ -2051,6 +2093,8 @@ lacp_update_portmap(struct lacp_softc *l if_link_state_change(&sc->sc_if, link); } + lagg_workq_add(lsc->lsc_workq, &lsc->lsc_work_linkspeed); + /* cleanup */ pm_act->pm_count = 0; memset(pm_act->pm_ports, 0, sizeof(pm_act->pm_ports)); @@ -2066,8 +2110,11 @@ lacp_disable_distributing(struct lacp_so KASSERT(LACP_LOCKED(lsc)); - LACP_DPRINTF((lsc, lacpp, "distributing disabled\n")); - CLR(lacpp->lp_actor.lpi_state, LACP_STATE_DISTRIBUTING); + if (ISSET(lacpp->lp_actor.lpi_state, LACP_STATE_DISTRIBUTING)) { + LAGG_LOG(lsc->lsc_softc, LOG_INFO, + "disable distributing on %s\n", LACP_PORT_XNAME(lacpp)); + CLR(lacpp->lp_actor.lpi_state, LACP_STATE_DISTRIBUTING); + } s = pserialize_read_enter(); act = LACP_PORTMAP_ACTIVE(lsc); @@ -2094,7 +2141,8 @@ lacp_enable_distributing(struct lacp_sof KASSERT(lacp_isactive(lsc, lacpp)); - LACP_DPRINTF((lsc, lacpp, "distributing enabled\n")); + LAGG_LOG(lsc->lsc_softc, LOG_INFO, + "enable distributing on %s\n", LACP_PORT_XNAME(lacpp)); SET(lacpp->lp_actor.lpi_state, LACP_STATE_DISTRIBUTING); lacp_suppress_distributing(lsc); lacp_update_portmap(lsc); @@ -2301,6 +2349,10 @@ lacp_sm_mux(struct lacp_softc *lsc, stru break; case LACP_MUX_ATTACHED: if (selected != LACP_SELECTED) { + if (selected == LACP_STANDBY) + LAGG_LOG(lsc->lsc_softc, LOG_INFO, + "detaching %s\n", + LACP_PORT_XNAME(lacpp)); next_state = LACP_MUX_DETACHED; } else if (lacp_isactive(lsc, lacpp) && p_sync) { next_state = LACP_MUX_COLLECTING; @@ -2354,8 +2406,10 @@ static void lacp_selected_update(struct lacp_softc *lsc, struct lacp_aggregator *la) { struct lacp_port *lacpp; + enum lacp_selected next_selected; + const char *msg; size_t nselected; - uint32_t media; + uint64_t linkspeed; KASSERT(LACP_LOCKED(lsc)); @@ -2363,27 +2417,34 @@ lacp_selected_update(struct lacp_softc * if (lacpp == NULL) return; - media = lacpp->lp_media; + linkspeed = lacpp->lp_linkspeed; nselected = 0; LIST_FOREACH(lacpp, &la->la_ports, lp_entry_la) { - if (nselected >= lsc->lsc_max_ports || - (!lsc->lsc_multi_linkspeed && media != lacpp->lp_media)) { - if (lacpp->lp_selected == LACP_SELECTED) - lacpp->lp_selected = LACP_STANDBY; + if (lacpp->lp_selected == LACP_UNSELECTED) continue; + + next_selected = LACP_SELECTED; + msg = " is selected"; + + if (nselected >= lsc->lsc_max_ports) { + next_selected = LACP_STANDBY; + msg = " is standby because of too many active ports"; } - switch (lacpp->lp_selected) { - case LACP_STANDBY: - lacpp->lp_selected = LACP_SELECTED; - /* fall through */ - case LACP_SELECTED: - nselected++; - break; - default: - /* do nothing */ - break; + if (!lsc->lsc_multi_linkspeed && + linkspeed != lacpp->lp_linkspeed) { + next_selected = LACP_STANDBY; + msg = " is standby because of link speed mismatch"; + } + + if (lacpp->lp_selected != next_selected) { + lacpp->lp_selected = next_selected; + LAGG_LOG(lsc->lsc_softc, LOG_INFO, + "%s%s\n", LACP_PORT_XNAME(lacpp), msg); } + + if (lacpp->lp_selected == LACP_SELECTED) + nselected++; } } @@ -2433,7 +2494,7 @@ lacp_select(struct lacp_softc *lsc, stru LACP_DPRINTF((lsc, lacpp, "aggregator lagid=%s\n", buf)); lacpp->lp_aggregator = la; - lacpp->lp_selected = LACP_STANDBY; + lacpp->lp_selected = LACP_READY; LIST_FOREACH(lacpp0, &la->la_ports, lp_entry_la) { if (lacp_port_priority_max(lacpp0, lacpp) == lacpp) { @@ -2501,19 +2562,23 @@ lacp_suppress_distributing(struct lacp_s { struct lacp_aggregator *la; struct lacp_port *lacpp; + bool marker_scheduled; KASSERT(LACP_LOCKED(lsc)); la = lsc->lsc_aggregator; + marker_scheduled = false; LIST_FOREACH(lacpp, &la->la_ports, lp_entry_la) { if (ISSET(lacpp->lp_actor.lpi_state, LACP_STATE_DISTRIBUTING)) { lagg_workq_add(lsc->lsc_workq, &lacpp->lp_work_marker); + marker_scheduled = true; } } + lsc->lsc_suppress_distributing = marker_scheduled; LACP_PTIMER_ARM(lsc, LACP_PTIMER_DISTRIBUTING, LACP_TRANSIT_DELAY); } @@ -2600,7 +2665,6 @@ lacp_marker_work(struct lagg_work *lw, v return; } SET(lacpp->lp_flags, LACP_PORT_MARK); - lsc->lsc_suppress_distributing = true; lp = lacpp->lp_laggport; bound = curlwp_bind(); lagg_port_getref(lp, &psref); @@ -2677,61 +2741,11 @@ lacp_dump_markertlv(const struct markerd } } -static uint32_t -lacp_ifmedia2lacpmedia(u_int ifmedia) -{ - uint32_t rv; - - switch (IFM_SUBTYPE(ifmedia)) { - case IFM_10_T: - case IFM_10_2: - case IFM_10_5: - case IFM_10_STP: - case IFM_10_FL: - rv = LACP_LINKSPEED_10; - break; - case IFM_100_TX: - case IFM_100_FX: - case IFM_100_T4: - case IFM_100_VG: - case IFM_100_T2: - rv = LACP_LINKSPEED_100; - break; - case IFM_1000_SX: - case IFM_1000_LX: - case IFM_1000_CX: - case IFM_1000_T: - case IFM_1000_BX10: - case IFM_1000_KX: - rv = LACP_LINKSPEED_1000; - break; - case IFM_2500_SX: - case IFM_2500_KX: - rv = LACP_LINKSPEED_2500; - break; - case IFM_5000_T: - rv = LACP_LINKSPEED_5000; - break; - case IFM_10G_LR: - case IFM_10G_SR: - case IFM_10G_CX4: - case IFM_10G_TWINAX: - case IFM_10G_TWINAX_LONG: - case IFM_10G_LRM: - case IFM_10G_T: - rv = LACP_LINKSPEED_10G; - break; - default: - rv = LACP_LINKSPEED_UNKNOWN; - } - - if (IFM_TYPE(ifmedia) == IFM_ETHER) - SET(rv, LACP_MEDIA_ETHER); - if ((ifmedia & IFM_FDX) != 0) - SET(rv, LACP_MEDIA_FDX); - - return rv; -} +/* + * lacp_linkstate: + * callback on link state changed. + * enable, disable or reset LACP processing on the physical port. + */ static void lacp_linkstate(struct lagg_proto_softc *xlsc, struct lagg_port *lp) @@ -2743,3 +2757,31 @@ lacp_linkstate(struct lagg_proto_softc * lacp_linkstate_ifnet_locked(xlsc, lp); IFNET_UNLOCK(lp->lp_ifp); } + +static void +lacp_linkspeed_work(struct lagg_work *lw __unused, void *xlsc) +{ + struct lacp_softc *lsc = (struct lacp_softc *)xlsc; + struct lagg_softc *sc = lsc->lsc_softc; + struct lacp_portmap *pm; + struct lagg_port *lp; + struct lacp_port *lacpp; + uint64_t linkspeed; + size_t act, i; + + linkspeed = 0; + + LACP_LOCK(lsc); + act = LACP_PORTMAP_ACTIVE(lsc); + pm = &lsc->lsc_portmaps[act]; + for (i = 0; i < pm->pm_count; i++) { + lp = pm->pm_ports[i]; + lacpp = lp->lp_proto_ctx; + linkspeed = MAX(linkspeed, lacpp->lp_linkspeed); + } + LACP_UNLOCK(lsc); + + LAGG_LOCK(sc); + lagg_set_linkspeed(sc, linkspeed); + LAGG_UNLOCK(sc); +} Index: src/sys/net/lagg/if_lagg_lacp.h diff -u src/sys/net/lagg/if_lagg_lacp.h:1.4 src/sys/net/lagg/if_lagg_lacp.h:1.4.4.1 --- src/sys/net/lagg/if_lagg_lacp.h:1.4 Thu Mar 31 02:04:50 2022 +++ src/sys/net/lagg/if_lagg_lacp.h Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: if_lagg_lacp.h,v 1.4 2022/03/31 02:04:50 yamaguchi Exp $ */ +/* $NetBSD: if_lagg_lacp.h,v 1.4.4.1 2024/10/03 11:53:46 martin Exp $ */ /* * Copyright (c) 2021 Internet Initiative Japan Inc. @@ -129,35 +129,4 @@ struct markerdu { struct tlvhdr mdu_tlv_term; uint8_t mdu_resv[90]; } __packed; - -/* - * lacp media: - * 1byte - * +-------+-------+-------+-------+ - * | media | speed | - * +-------+-------+-------+-------+ - */ - -enum lacp_linkspeed { - LACP_LINKSPEED_UNKNOWN = 0, - LACP_LINKSPEED_10, - LACP_LINKSPEED_100, - LACP_LINKSPEED_1000, - LACP_LINKSPEED_2500, - LACP_LINKSPEED_5000, - LACP_LINKSPEED_10G, - LACP_LINKSPEED_25G, - LACP_LINKSPEED_40G, - LACP_LINKSPEED_50G, - LACP_LINKSPEED_56G, - LACP_LINKSPEED_100G, - LACP_LINKSPEED_200G, -}; - -#define LACP_MEDIA_OFFSET 24 -#define LACP_MEDIA_MASK 0xff000000U -#define LACP_MEDIA_ETHER (__BIT(0) << LACP_MEDIA_OFFSET) -#define LACP_MEDIA_FDX (__BIT(1) << LACP_MEDIA_OFFSET) -#define LACP_MEDIA_DEFAULT (LACP_LINKSPEED_UNKNOWN | \ - LACP_MEDIA_ETHER | LACP_MEDIA_FDX) #endif Index: src/sys/net/lagg/if_laggproto.c diff -u src/sys/net/lagg/if_laggproto.c:1.6 src/sys/net/lagg/if_laggproto.c:1.6.4.1 --- src/sys/net/lagg/if_laggproto.c:1.6 Thu Mar 31 07:59:05 2022 +++ src/sys/net/lagg/if_laggproto.c Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: if_laggproto.c,v 1.6 2022/03/31 07:59:05 yamaguchi Exp $ */ +/* $NetBSD: if_laggproto.c,v 1.6.4.1 2024/10/03 11:53:46 martin Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD @@ -29,7 +29,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_laggproto.c,v 1.6 2022/03/31 07:59:05 yamaguchi Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_laggproto.c,v 1.6.4.1 2024/10/03 11:53:46 martin Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -57,15 +57,16 @@ struct lagg_proto_softc { size_t psc_ctxsiz; void *psc_ctx; size_t psc_nactports; + struct workqueue *psc_workq; + struct lagg_work psc_work_linkspeed; }; /* * Locking notes: * - Items of struct lagg_proto_softc is protected by * psc_lock (an adaptive mutex) - * - psc_ports is protected by pserialize (psc_psz) - * - Updates of psc_ports is serialized by sc_lock in - * struct lagg_softc + * - psc_ports is protected by pselialize (psc_psz) and + * it updates exclusively by LAGG_PROTO_LOCK. * - Other locking notes are described in if_laggproto.h */ @@ -90,7 +91,9 @@ struct lagg_lb { struct lagg_proto_port { struct pslist_entry lpp_entry; struct lagg_port *lpp_laggport; + uint64_t lpp_linkspeed; bool lpp_active; + bool lpp_running; }; #define LAGG_PROTO_LOCK(_psc) mutex_enter(&(_psc)->psc_lock) @@ -107,6 +110,11 @@ static void lagg_proto_remove_port(struc static struct lagg_port * lagg_link_active(struct lagg_proto_softc *psc, struct lagg_proto_port *, struct psref *); +static void lagg_fail_linkspeed_work(struct lagg_work *, void *); +static void lagg_lb_linkspeed_work(struct lagg_work*, + void *); +static void lagg_common_linkstate(struct lagg_proto_softc *, + struct lagg_port *); static inline struct lagg_portmap * lagg_portmap_active(struct lagg_portmaps *maps) @@ -124,7 +132,6 @@ lagg_portmap_next(struct lagg_portmaps * size_t i; i = atomic_load_consume(&maps->maps_activepmap); - i &= 0x1; i ^= 0x1; return &maps->maps_pmap[i]; @@ -146,6 +153,7 @@ static struct lagg_proto_softc * lagg_proto_alloc(lagg_proto pr, struct lagg_softc *sc) { struct lagg_proto_softc *psc; + char xnamebuf[MAXCOMLEN]; size_t ctxsiz; switch (pr) { @@ -163,9 +171,20 @@ lagg_proto_alloc(lagg_proto pr, struct l if (psc == NULL) return NULL; + snprintf(xnamebuf, sizeof(xnamebuf), "%s.proto", + sc->sc_if.if_xname); + psc->psc_workq = lagg_workq_create(xnamebuf, + PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); + if (psc->psc_workq == NULL) { + LAGG_LOG(sc, LOG_ERR, "workqueue create failed\n"); + kmem_free(psc, sizeof(*psc)); + return NULL; + } + if (ctxsiz > 0) { psc->psc_ctx = kmem_zalloc(ctxsiz, KM_NOSLEEP); if (psc->psc_ctx == NULL) { + lagg_workq_destroy(psc->psc_workq); kmem_free(psc, sizeof(*psc)); return NULL; } @@ -185,8 +204,11 @@ static void lagg_proto_free(struct lagg_proto_softc *psc) { + lagg_workq_wait(psc->psc_workq, &psc->psc_work_linkspeed); pserialize_destroy(psc->psc_psz); mutex_destroy(&psc->psc_lock); + lagg_workq_destroy(psc->psc_workq); + PSLIST_DESTROY(&psc->psc_ports); if (psc->psc_ctxsiz > 0) kmem_free(psc->psc_ctx, psc->psc_ctxsiz); @@ -251,6 +273,7 @@ lagg_common_freeport(struct lagg_proto_s struct lagg_proto_port *pport; pport = lp->lp_proto_ctx; + KASSERT(!pport->lpp_running); lp->lp_proto_ctx = NULL; kmem_free(pport, sizeof(*pport)); @@ -299,8 +322,12 @@ lagg_proto_remove_port(struct lagg_proto LAGG_PROTO_LOCK(psc); PSLIST_WRITER_REMOVE(pport, lpp_entry); - pserialize_perform(psc->psc_psz); LAGG_PROTO_UNLOCK(psc); + pserialize_perform(psc->psc_psz); + + /* re-initialize for reuse */ + PSLIST_ENTRY_DESTROY(pport, lpp_entry); + PSLIST_ENTRY_INIT(pport, lpp_entry); } void @@ -311,6 +338,10 @@ lagg_common_startport(struct lagg_proto_ pport = lp->lp_proto_ctx; lagg_proto_insert_port(psc, pport); + LAGG_PROTO_LOCK(psc); + pport->lpp_running = true; + LAGG_PROTO_UNLOCK(psc); + lagg_common_linkstate(psc, lp); } @@ -321,6 +352,11 @@ lagg_common_stopport(struct lagg_proto_s struct ifnet *ifp; pport = lp->lp_proto_ctx; + + LAGG_PROTO_LOCK(psc); + pport->lpp_running = false; + LAGG_PROTO_UNLOCK(psc); + lagg_proto_remove_port(psc, pport); if (pport->lpp_active) { @@ -334,22 +370,46 @@ lagg_common_stopport(struct lagg_proto_s pport->lpp_active = false; } + + lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); +} +static void +lagg_common_linkstate(struct lagg_proto_softc *psc, struct lagg_port *lp) +{ + + IFNET_ASSERT_UNLOCKED(lp->lp_ifp); + + IFNET_LOCK(lp->lp_ifp); + lagg_common_linkstate_ifnet_locked(psc, lp); + IFNET_UNLOCK(lp->lp_ifp); } void -lagg_common_linkstate(struct lagg_proto_softc *psc, struct lagg_port *lp) +lagg_common_linkstate_ifnet_locked(struct lagg_proto_softc *psc, struct lagg_port *lp) { struct lagg_proto_port *pport; - struct ifnet *ifp; + struct ifnet *ifp, *ifp_port; + struct ifmediareq ifmr; + uint64_t linkspeed; bool is_active; + int error; pport = lp->lp_proto_ctx; is_active = lagg_portactive(lp); + ifp_port = lp->lp_ifp; + + KASSERT(IFNET_LOCKED(ifp_port)); - if (pport->lpp_active == is_active) + LAGG_PROTO_LOCK(psc); + if (!pport->lpp_running || + pport->lpp_active == is_active) { + LAGG_PROTO_UNLOCK(psc); return; + } ifp = &psc->psc_softc->sc_if; + pport->lpp_active = is_active; + if (is_active) { psc->psc_nactports++; if (psc->psc_nactports == 1) @@ -361,8 +421,20 @@ lagg_common_linkstate(struct lagg_proto_ if (psc->psc_nactports == 0) if_link_state_change(ifp, LINK_STATE_DOWN); } + LAGG_PROTO_UNLOCK(psc); + + memset(&ifmr, 0, sizeof(ifmr)); + error = if_ioctl(ifp_port, SIOCGIFMEDIA, (void *)&ifmr); + if (error == 0) { + linkspeed = ifmedia_baudrate(ifmr.ifm_active); + } else { + linkspeed = 0; + } - atomic_store_relaxed(&pport->lpp_active, is_active); + LAGG_PROTO_LOCK(psc); + pport->lpp_linkspeed = linkspeed; + LAGG_PROTO_UNLOCK(psc); + lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); } void @@ -392,6 +464,8 @@ lagg_fail_attach(struct lagg_softc *sc, fovr = psc->psc_ctx; fovr->fo_rx_all = true; + lagg_work_set(&psc->psc_work_linkspeed, + lagg_fail_linkspeed_work, psc); *xpsc = psc; return 0; @@ -510,6 +584,33 @@ lagg_fail_ioctl(struct lagg_proto_softc return error; } +void +lagg_fail_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) +{ + struct lagg_proto_softc *psc = xpsc; + struct lagg_proto_port *pport; + struct lagg_port *lp; + struct psref psref; + uint64_t linkspeed; + + kpreempt_disable(); + lp = lagg_link_active(psc, NULL, &psref); + if (lp != NULL) { + pport = lp->lp_proto_ctx; + LAGG_PROTO_LOCK(psc); + linkspeed = pport->lpp_linkspeed; + LAGG_PROTO_UNLOCK(psc); + lagg_port_putref(lp, &psref); + } else { + linkspeed = 0; + } + kpreempt_enable(); + + LAGG_LOCK(psc->psc_softc); + lagg_set_linkspeed(psc->psc_softc, linkspeed); + LAGG_UNLOCK(psc->psc_softc); +} + int lagg_lb_attach(struct lagg_softc *sc, struct lagg_proto_softc **xpsc) { @@ -522,6 +623,8 @@ lagg_lb_attach(struct lagg_softc *sc, st lb = psc->psc_ctx; lb->lb_pmaps.maps_activepmap = 0; + lagg_work_set(&psc->psc_work_linkspeed, + lagg_lb_linkspeed_work, psc); *xpsc = psc; return 0; @@ -550,8 +653,8 @@ lagg_lb_startport(struct lagg_proto_soft pm_next->pm_nports = n; lagg_portmap_switch(&lb->lb_pmaps); - pserialize_perform(psc->psc_psz); LAGG_PROTO_UNLOCK(psc); + pserialize_perform(psc->psc_psz); } void @@ -576,9 +679,11 @@ lagg_lb_stopport(struct lagg_proto_softc n++; } + pm_next->pm_nports = n; + lagg_portmap_switch(&lb->lb_pmaps); - pserialize_perform(psc->psc_psz); LAGG_PROTO_UNLOCK(psc); + pserialize_perform(psc->psc_psz); lagg_common_stopport(psc, lp); } @@ -595,14 +700,18 @@ lagg_lb_transmit(struct lagg_proto_softc int s; lb = psc->psc_ctx; - hash = lagg_hashmbuf(psc->psc_softc, m); + hash = lagg_hashmbuf(psc->psc_softc, m); s = pserialize_read_enter(); pm = lagg_portmap_active(&lb->lb_pmaps); - hash %= pm->pm_nports; - lp0 = pm->pm_ports[hash]; - lp = lagg_link_active(psc, lp0->lp_proto_ctx, &psref); + if (__predict_true(pm->pm_nports != 0)) { + hash %= pm->pm_nports; + lp0 = pm->pm_ports[hash]; + lp = lagg_link_active(psc, lp0->lp_proto_ctx, &psref); + } else { + lp = NULL; + } pserialize_read_exit(s); @@ -640,3 +749,27 @@ lagg_lb_portstat(struct lagg_proto_softc LAGG_PORT_COLLECTING | LAGG_PORT_DISTRIBUTING); } } + +static void +lagg_lb_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) +{ + struct lagg_proto_softc *psc = xpsc; + struct lagg_proto_port *pport; + uint64_t linkspeed, l; + + linkspeed = 0; + + LAGG_PROTO_LOCK(psc); /* acquired to refer lpp_linkspeed */ + PSLIST_READER_FOREACH(pport, &psc->psc_ports, + struct lagg_proto_port, lpp_entry) { + if (pport->lpp_active) { + l = pport->lpp_linkspeed; + linkspeed = MAX(linkspeed, l); + } + } + LAGG_PROTO_UNLOCK(psc); + + LAGG_LOCK(psc->psc_softc); + lagg_set_linkspeed(psc->psc_softc, linkspeed); + LAGG_UNLOCK(psc->psc_softc); +} Index: src/sys/net/lagg/if_laggproto.h diff -u src/sys/net/lagg/if_laggproto.h:1.18 src/sys/net/lagg/if_laggproto.h:1.18.4.1 --- src/sys/net/lagg/if_laggproto.h:1.18 Sun Jun 26 17:55:24 2022 +++ src/sys/net/lagg/if_laggproto.h Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: if_laggproto.h,v 1.18 2022/06/26 17:55:24 riastradh Exp $ */ +/* $NetBSD: if_laggproto.h,v 1.18.4.1 2024/10/03 11:53:46 martin Exp $ */ /* * Copyright (c) 2021 Internet Initiative Japan Inc. @@ -146,6 +146,7 @@ struct lagg_vlantag { struct lagg_softc { kmutex_t sc_lock; struct ifmedia sc_media; + uint64_t sc_media_active; u_char sc_iftype; /* interface link-layer address */ @@ -286,6 +287,7 @@ void lagg_port_putref(struct lagg_port void lagg_output(struct lagg_softc *, struct lagg_port *, struct mbuf *); uint32_t lagg_hashmbuf(struct lagg_softc *, struct mbuf *); +void lagg_set_linkspeed(struct lagg_softc *, uint64_t); void lagg_common_detach(struct lagg_proto_softc *); int lagg_common_allocport(struct lagg_proto_softc *, @@ -296,7 +298,7 @@ void lagg_common_startport(struct lagg_ struct lagg_port *); void lagg_common_stopport(struct lagg_proto_softc *, struct lagg_port *); -void lagg_common_linkstate(struct lagg_proto_softc *, +void lagg_common_linkstate_ifnet_locked(struct lagg_proto_softc *, struct lagg_port *); int lagg_none_attach(struct lagg_softc *, Index: src/tests/net/if_lagg/t_lagg.sh diff -u src/tests/net/if_lagg/t_lagg.sh:1.8.2.1 src/tests/net/if_lagg/t_lagg.sh:1.8.2.2 --- src/tests/net/if_lagg/t_lagg.sh:1.8.2.1 Thu Oct 19 07:23:50 2023 +++ src/tests/net/if_lagg/t_lagg.sh Thu Oct 3 11:53:46 2024 @@ -1,4 +1,4 @@ -# $NetBSD: t_lagg.sh,v 1.8.2.1 2023/10/19 07:23:50 martin Exp $ +# $NetBSD: t_lagg.sh,v 1.8.2.2 2024/10/03 11:53:46 martin Exp $ # # Copyright (c) 2021 Internet Initiative Japan Inc. # All rights reserved. @@ -214,7 +214,7 @@ lagg_ifconfig_body() lagg_ifconfig_cleanup() { - $DEBG && dump + $DEBUG && dump cleanup } @@ -392,7 +392,7 @@ lagg_mtu_body() atf_check -s exit:0 -o match:"mtu *$mtu_big" rump.ifconfig shmif1 atf_check -s exit:0 -o match:"mtu *$mtu_small" rump.ifconfig shmif2 - # copy MTU from 1st port + # copy MTU to 1st added port $atf_ifconfig lagg0 laggport shmif0 atf_check -s exit:0 -o match:"mtu *$mtu_lagg" rump.ifconfig lagg0 atf_check -s exit:0 -o match:"mtu *$mtu_lagg" rump.ifconfig shmif0