1*ee9ef17bSrin /* $NetBSD: if_laggproto.c,v 1.16 2024/09/26 06:08:24 rin Exp $ */ 2f0101d0eSyamaguchi 3f0101d0eSyamaguchi /*- 4f0101d0eSyamaguchi * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5f0101d0eSyamaguchi * 6f0101d0eSyamaguchi * Copyright (c)2021 Internet Initiative Japan, Inc. 7f0101d0eSyamaguchi * All rights reserved. 8f0101d0eSyamaguchi * 9f0101d0eSyamaguchi * Redistribution and use in source and binary forms, with or without 10f0101d0eSyamaguchi * modification, are permitted provided that the following conditions 11f0101d0eSyamaguchi * are met: 12f0101d0eSyamaguchi * 1. Redistributions of source code must retain the above copyright 13f0101d0eSyamaguchi * notice, this list of conditions and the following disclaimer. 14f0101d0eSyamaguchi * 2. Redistributions in binary form must reproduce the above copyright 15f0101d0eSyamaguchi * notice, this list of conditions and the following disclaimer in the 16f0101d0eSyamaguchi * documentation and/or other materials provided with the distribution. 17f0101d0eSyamaguchi * 18f0101d0eSyamaguchi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19f0101d0eSyamaguchi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20f0101d0eSyamaguchi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21f0101d0eSyamaguchi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22f0101d0eSyamaguchi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23f0101d0eSyamaguchi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24f0101d0eSyamaguchi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25f0101d0eSyamaguchi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26f0101d0eSyamaguchi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27f0101d0eSyamaguchi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28f0101d0eSyamaguchi * SUCH DAMAGE. 29f0101d0eSyamaguchi */ 30f0101d0eSyamaguchi 31f0101d0eSyamaguchi #include <sys/cdefs.h> 32*ee9ef17bSrin __KERNEL_RCSID(0, "$NetBSD: if_laggproto.c,v 1.16 2024/09/26 06:08:24 rin Exp $"); 33f0101d0eSyamaguchi 34f0101d0eSyamaguchi #include <sys/param.h> 35f0101d0eSyamaguchi #include <sys/types.h> 36f0101d0eSyamaguchi 37f0101d0eSyamaguchi #include <sys/evcnt.h> 38f0101d0eSyamaguchi #include <sys/kmem.h> 39f0101d0eSyamaguchi #include <sys/mbuf.h> 40f0101d0eSyamaguchi #include <sys/mutex.h> 41f0101d0eSyamaguchi #include <sys/pslist.h> 42f0101d0eSyamaguchi #include <sys/syslog.h> 43f0101d0eSyamaguchi #include <sys/workqueue.h> 44f0101d0eSyamaguchi 45f0101d0eSyamaguchi #include <net/if.h> 46f0101d0eSyamaguchi #include <net/if_ether.h> 47f0101d0eSyamaguchi #include <net/if_media.h> 48f0101d0eSyamaguchi 49f0101d0eSyamaguchi #include <net/lagg/if_lagg.h> 50f0101d0eSyamaguchi #include <net/lagg/if_laggproto.h> 51f0101d0eSyamaguchi 52f0101d0eSyamaguchi struct lagg_proto_softc { 53f0101d0eSyamaguchi struct lagg_softc *psc_softc; 54f0101d0eSyamaguchi struct pslist_head psc_ports; 55f0101d0eSyamaguchi kmutex_t psc_lock; 56f0101d0eSyamaguchi pserialize_t psc_psz; 57f0101d0eSyamaguchi size_t psc_ctxsiz; 58f0101d0eSyamaguchi void *psc_ctx; 59f0101d0eSyamaguchi size_t psc_nactports; 6071d2a73aSyamaguchi struct workqueue *psc_workq; 6171d2a73aSyamaguchi struct lagg_work psc_work_linkspeed; 62f0101d0eSyamaguchi }; 63f0101d0eSyamaguchi 64f0101d0eSyamaguchi /* 65f0101d0eSyamaguchi * Locking notes: 66f0101d0eSyamaguchi * - Items of struct lagg_proto_softc is protected by 67f0101d0eSyamaguchi * psc_lock (an adaptive mutex) 6887bf012bSyamaguchi * - psc_ports is protected by pselialize (psc_psz) and 6987bf012bSyamaguchi * it updates exclusively by LAGG_PROTO_LOCK. 70f0101d0eSyamaguchi * - Other locking notes are described in if_laggproto.h 71f0101d0eSyamaguchi */ 72f0101d0eSyamaguchi 73f0101d0eSyamaguchi struct lagg_failover { 74f0101d0eSyamaguchi bool fo_rx_all; 75f0101d0eSyamaguchi }; 76f0101d0eSyamaguchi 77f0101d0eSyamaguchi struct lagg_portmap { 78f0101d0eSyamaguchi struct lagg_port *pm_ports[LAGG_MAX_PORTS]; 79f0101d0eSyamaguchi size_t pm_nports; 80f0101d0eSyamaguchi }; 81f0101d0eSyamaguchi 82f0101d0eSyamaguchi struct lagg_portmaps { 83f0101d0eSyamaguchi struct lagg_portmap maps_pmap[2]; 84f0101d0eSyamaguchi size_t maps_activepmap; 85f0101d0eSyamaguchi }; 86f0101d0eSyamaguchi 87f0101d0eSyamaguchi struct lagg_lb { 88f0101d0eSyamaguchi struct lagg_portmaps lb_pmaps; 89f0101d0eSyamaguchi }; 90f0101d0eSyamaguchi 91f0101d0eSyamaguchi struct lagg_proto_port { 92f0101d0eSyamaguchi struct pslist_entry lpp_entry; 93f0101d0eSyamaguchi struct lagg_port *lpp_laggport; 9471d2a73aSyamaguchi uint64_t lpp_linkspeed; 95f0101d0eSyamaguchi bool lpp_active; 9671d2a73aSyamaguchi bool lpp_running; 97f0101d0eSyamaguchi }; 98f0101d0eSyamaguchi 99f0101d0eSyamaguchi #define LAGG_PROTO_LOCK(_psc) mutex_enter(&(_psc)->psc_lock) 100f0101d0eSyamaguchi #define LAGG_PROTO_UNLOCK(_psc) mutex_exit(&(_psc)->psc_lock) 101f0101d0eSyamaguchi #define LAGG_PROTO_LOCKED(_psc) mutex_owned(&(_psc)->psc_lock) 102f0101d0eSyamaguchi 103f0101d0eSyamaguchi static struct lagg_proto_softc * 104f0101d0eSyamaguchi lagg_proto_alloc(lagg_proto, struct lagg_softc *); 105f0101d0eSyamaguchi static void lagg_proto_free(struct lagg_proto_softc *); 106f0101d0eSyamaguchi static void lagg_proto_insert_port(struct lagg_proto_softc *, 107f0101d0eSyamaguchi struct lagg_proto_port *); 108f0101d0eSyamaguchi static void lagg_proto_remove_port(struct lagg_proto_softc *, 109f0101d0eSyamaguchi struct lagg_proto_port *); 110f0101d0eSyamaguchi static struct lagg_port * 111f0101d0eSyamaguchi lagg_link_active(struct lagg_proto_softc *psc, 112f0101d0eSyamaguchi struct lagg_proto_port *, struct psref *); 11371d2a73aSyamaguchi static void lagg_fail_linkspeed_work(struct lagg_work *, void *); 11471d2a73aSyamaguchi static void lagg_lb_linkspeed_work(struct lagg_work*, 11571d2a73aSyamaguchi void *); 116d3cb38e4Syamaguchi static void lagg_common_linkstate(struct lagg_proto_softc *, 117d3cb38e4Syamaguchi struct lagg_port *); 118f0101d0eSyamaguchi 119f0101d0eSyamaguchi static inline struct lagg_portmap * 120f0101d0eSyamaguchi lagg_portmap_active(struct lagg_portmaps *maps) 121f0101d0eSyamaguchi { 122f0101d0eSyamaguchi size_t i; 123f0101d0eSyamaguchi 124f0101d0eSyamaguchi i = atomic_load_consume(&maps->maps_activepmap); 125f0101d0eSyamaguchi 126f0101d0eSyamaguchi return &maps->maps_pmap[i]; 127f0101d0eSyamaguchi } 128f0101d0eSyamaguchi 129f0101d0eSyamaguchi static inline struct lagg_portmap * 130f0101d0eSyamaguchi lagg_portmap_next(struct lagg_portmaps *maps) 131f0101d0eSyamaguchi { 132f0101d0eSyamaguchi size_t i; 133f0101d0eSyamaguchi 134f0101d0eSyamaguchi i = atomic_load_consume(&maps->maps_activepmap); 135f0101d0eSyamaguchi i ^= 0x1; 136f0101d0eSyamaguchi 137f0101d0eSyamaguchi return &maps->maps_pmap[i]; 138f0101d0eSyamaguchi } 139f0101d0eSyamaguchi 140f0101d0eSyamaguchi static inline void 141f0101d0eSyamaguchi lagg_portmap_switch(struct lagg_portmaps *maps) 142f0101d0eSyamaguchi { 143f0101d0eSyamaguchi size_t i; 144f0101d0eSyamaguchi 145f0101d0eSyamaguchi i = atomic_load_consume(&maps->maps_activepmap); 146f0101d0eSyamaguchi i &= 0x1; 147f0101d0eSyamaguchi i ^= 0x1; 148f0101d0eSyamaguchi 149f0101d0eSyamaguchi atomic_store_release(&maps->maps_activepmap, i); 150f0101d0eSyamaguchi } 151f0101d0eSyamaguchi 152f0101d0eSyamaguchi static struct lagg_proto_softc * 153f0101d0eSyamaguchi lagg_proto_alloc(lagg_proto pr, struct lagg_softc *sc) 154f0101d0eSyamaguchi { 155f0101d0eSyamaguchi struct lagg_proto_softc *psc; 15671d2a73aSyamaguchi char xnamebuf[MAXCOMLEN]; 157f0101d0eSyamaguchi size_t ctxsiz; 158f0101d0eSyamaguchi 159f0101d0eSyamaguchi switch (pr) { 160f0101d0eSyamaguchi case LAGG_PROTO_FAILOVER: 161f0101d0eSyamaguchi ctxsiz = sizeof(struct lagg_failover); 162f0101d0eSyamaguchi break; 163f0101d0eSyamaguchi case LAGG_PROTO_LOADBALANCE: 164f0101d0eSyamaguchi ctxsiz = sizeof(struct lagg_lb); 165f0101d0eSyamaguchi break; 166f0101d0eSyamaguchi default: 167f0101d0eSyamaguchi ctxsiz = 0; 168f0101d0eSyamaguchi } 169f0101d0eSyamaguchi 170f0101d0eSyamaguchi psc = kmem_zalloc(sizeof(*psc), KM_NOSLEEP); 171f0101d0eSyamaguchi if (psc == NULL) 172f0101d0eSyamaguchi return NULL; 173f0101d0eSyamaguchi 174*ee9ef17bSrin snprintf(xnamebuf, sizeof(xnamebuf), "%s.proto", 175*ee9ef17bSrin sc->sc_if.if_xname); 17671d2a73aSyamaguchi psc->psc_workq = lagg_workq_create(xnamebuf, 17771d2a73aSyamaguchi PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); 17871d2a73aSyamaguchi if (psc->psc_workq == NULL) { 17971d2a73aSyamaguchi LAGG_LOG(sc, LOG_ERR, "workqueue create failed\n"); 18071d2a73aSyamaguchi kmem_free(psc, sizeof(*psc)); 18171d2a73aSyamaguchi return NULL; 18271d2a73aSyamaguchi } 18371d2a73aSyamaguchi 184f0101d0eSyamaguchi if (ctxsiz > 0) { 185f0101d0eSyamaguchi psc->psc_ctx = kmem_zalloc(ctxsiz, KM_NOSLEEP); 186f0101d0eSyamaguchi if (psc->psc_ctx == NULL) { 18771d2a73aSyamaguchi lagg_workq_destroy(psc->psc_workq); 188f0101d0eSyamaguchi kmem_free(psc, sizeof(*psc)); 189f0101d0eSyamaguchi return NULL; 190f0101d0eSyamaguchi } 191f0101d0eSyamaguchi 192f0101d0eSyamaguchi psc->psc_ctxsiz = ctxsiz; 193f0101d0eSyamaguchi } 194f0101d0eSyamaguchi 195f0101d0eSyamaguchi PSLIST_INIT(&psc->psc_ports); 196f0101d0eSyamaguchi psc->psc_psz = pserialize_create(); 197f0101d0eSyamaguchi mutex_init(&psc->psc_lock, MUTEX_DEFAULT, IPL_SOFTNET); 198f0101d0eSyamaguchi psc->psc_softc = sc; 199f0101d0eSyamaguchi 200f0101d0eSyamaguchi return psc; 201f0101d0eSyamaguchi } 202f0101d0eSyamaguchi 203f0101d0eSyamaguchi static void 204f0101d0eSyamaguchi lagg_proto_free(struct lagg_proto_softc *psc) 205f0101d0eSyamaguchi { 206f0101d0eSyamaguchi 20771d2a73aSyamaguchi lagg_workq_wait(psc->psc_workq, &psc->psc_work_linkspeed); 208f0101d0eSyamaguchi pserialize_destroy(psc->psc_psz); 209f0101d0eSyamaguchi mutex_destroy(&psc->psc_lock); 21071d2a73aSyamaguchi lagg_workq_destroy(psc->psc_workq); 21196257eafSyamaguchi PSLIST_DESTROY(&psc->psc_ports); 212f0101d0eSyamaguchi 213f0101d0eSyamaguchi if (psc->psc_ctxsiz > 0) 214f0101d0eSyamaguchi kmem_free(psc->psc_ctx, psc->psc_ctxsiz); 215f0101d0eSyamaguchi 216f0101d0eSyamaguchi kmem_free(psc, sizeof(*psc)); 217f0101d0eSyamaguchi } 218f0101d0eSyamaguchi 219f0101d0eSyamaguchi static struct lagg_port * 220f0101d0eSyamaguchi lagg_link_active(struct lagg_proto_softc *psc, 221f0101d0eSyamaguchi struct lagg_proto_port *pport, struct psref *psref) 222f0101d0eSyamaguchi { 223f0101d0eSyamaguchi struct lagg_port *lp; 224f0101d0eSyamaguchi int s; 225f0101d0eSyamaguchi 226f0101d0eSyamaguchi lp = NULL; 227f0101d0eSyamaguchi s = pserialize_read_enter(); 228f0101d0eSyamaguchi 229f0101d0eSyamaguchi for (;pport != NULL; 230f0101d0eSyamaguchi pport = PSLIST_READER_NEXT(pport, 231f0101d0eSyamaguchi struct lagg_proto_port, lpp_entry)) { 232f0101d0eSyamaguchi if (atomic_load_relaxed(&pport->lpp_active)) { 233f0101d0eSyamaguchi lp = pport->lpp_laggport; 234f0101d0eSyamaguchi goto done; 235f0101d0eSyamaguchi } 236f0101d0eSyamaguchi } 237f0101d0eSyamaguchi 238f0101d0eSyamaguchi PSLIST_READER_FOREACH(pport, &psc->psc_ports, 239f0101d0eSyamaguchi struct lagg_proto_port, lpp_entry) { 240f0101d0eSyamaguchi if (atomic_load_relaxed(&pport->lpp_active)) { 241f0101d0eSyamaguchi lp = pport->lpp_laggport; 242f0101d0eSyamaguchi break; 243f0101d0eSyamaguchi } 244f0101d0eSyamaguchi } 245f0101d0eSyamaguchi done: 246f0101d0eSyamaguchi if (lp != NULL) 247f0101d0eSyamaguchi lagg_port_getref(lp, psref); 248f0101d0eSyamaguchi pserialize_read_exit(s); 249f0101d0eSyamaguchi 250f0101d0eSyamaguchi return lp; 251f0101d0eSyamaguchi } 252f0101d0eSyamaguchi 253f0101d0eSyamaguchi int 254f0101d0eSyamaguchi lagg_common_allocport(struct lagg_proto_softc *psc, struct lagg_port *lp) 255f0101d0eSyamaguchi { 256f0101d0eSyamaguchi struct lagg_proto_port *pport; 257f0101d0eSyamaguchi 258f0101d0eSyamaguchi KASSERT(LAGG_LOCKED(psc->psc_softc)); 259f0101d0eSyamaguchi 260f0101d0eSyamaguchi pport = kmem_zalloc(sizeof(*pport), KM_NOSLEEP); 261f0101d0eSyamaguchi if (pport == NULL) 262f0101d0eSyamaguchi return ENOMEM; 263f0101d0eSyamaguchi 264f0101d0eSyamaguchi PSLIST_ENTRY_INIT(pport, lpp_entry); 265f0101d0eSyamaguchi pport->lpp_laggport = lp; 266f0101d0eSyamaguchi lp->lp_proto_ctx = (void *)pport; 267f0101d0eSyamaguchi return 0; 268f0101d0eSyamaguchi } 269f0101d0eSyamaguchi 270f0101d0eSyamaguchi void 271f0101d0eSyamaguchi lagg_common_freeport(struct lagg_proto_softc *psc, struct lagg_port *lp) 272f0101d0eSyamaguchi { 273f0101d0eSyamaguchi struct lagg_proto_port *pport; 274f0101d0eSyamaguchi 275f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 27671d2a73aSyamaguchi KASSERT(!pport->lpp_running); 277f0101d0eSyamaguchi lp->lp_proto_ctx = NULL; 278f0101d0eSyamaguchi 279f0101d0eSyamaguchi kmem_free(pport, sizeof(*pport)); 280f0101d0eSyamaguchi } 281f0101d0eSyamaguchi 282f0101d0eSyamaguchi static void 283f0101d0eSyamaguchi lagg_proto_insert_port(struct lagg_proto_softc *psc, 284f0101d0eSyamaguchi struct lagg_proto_port *pport) 285f0101d0eSyamaguchi { 286f0101d0eSyamaguchi struct lagg_proto_port *pport0; 287f0101d0eSyamaguchi struct lagg_port *lp, *lp0; 288f0101d0eSyamaguchi bool insert_after; 289f0101d0eSyamaguchi 290f0101d0eSyamaguchi insert_after = false; 291f0101d0eSyamaguchi lp = pport->lpp_laggport; 292f0101d0eSyamaguchi 293f0101d0eSyamaguchi LAGG_PROTO_LOCK(psc); 294f0101d0eSyamaguchi PSLIST_WRITER_FOREACH(pport0, &psc->psc_ports, 295f0101d0eSyamaguchi struct lagg_proto_port, lpp_entry) { 296f0101d0eSyamaguchi lp0 = pport0->lpp_laggport; 297f0101d0eSyamaguchi if (lp0->lp_prio > lp->lp_prio) 298f0101d0eSyamaguchi break; 299f0101d0eSyamaguchi 300f0101d0eSyamaguchi if (PSLIST_WRITER_NEXT(pport0, 301f0101d0eSyamaguchi struct lagg_proto_port, lpp_entry) == NULL) { 302f0101d0eSyamaguchi insert_after = true; 303f0101d0eSyamaguchi break; 304f0101d0eSyamaguchi } 305f0101d0eSyamaguchi } 306f0101d0eSyamaguchi 307f0101d0eSyamaguchi if (pport0 == NULL) { 308f0101d0eSyamaguchi PSLIST_WRITER_INSERT_HEAD(&psc->psc_ports, pport, 309f0101d0eSyamaguchi lpp_entry); 310f0101d0eSyamaguchi } else if (insert_after) { 311f0101d0eSyamaguchi PSLIST_WRITER_INSERT_AFTER(pport0, pport, lpp_entry); 312f0101d0eSyamaguchi } else { 313f0101d0eSyamaguchi PSLIST_WRITER_INSERT_BEFORE(pport0, pport, lpp_entry); 314f0101d0eSyamaguchi } 315f0101d0eSyamaguchi LAGG_PROTO_UNLOCK(psc); 316f0101d0eSyamaguchi } 317f0101d0eSyamaguchi 318f0101d0eSyamaguchi static void 319f0101d0eSyamaguchi lagg_proto_remove_port(struct lagg_proto_softc *psc, 320f0101d0eSyamaguchi struct lagg_proto_port *pport) 321f0101d0eSyamaguchi { 322f0101d0eSyamaguchi 323f0101d0eSyamaguchi LAGG_PROTO_LOCK(psc); 324f0101d0eSyamaguchi PSLIST_WRITER_REMOVE(pport, lpp_entry); 325f0101d0eSyamaguchi LAGG_PROTO_UNLOCK(psc); 32648a2a44cSyamaguchi pserialize_perform(psc->psc_psz); 32796257eafSyamaguchi 32896257eafSyamaguchi /* re-initialize for reuse */ 32996257eafSyamaguchi PSLIST_ENTRY_DESTROY(pport, lpp_entry); 33096257eafSyamaguchi PSLIST_ENTRY_INIT(pport, lpp_entry); 331f0101d0eSyamaguchi } 332f0101d0eSyamaguchi 333f0101d0eSyamaguchi void 334f0101d0eSyamaguchi lagg_common_startport(struct lagg_proto_softc *psc, struct lagg_port *lp) 335f0101d0eSyamaguchi { 336f0101d0eSyamaguchi struct lagg_proto_port *pport; 337f0101d0eSyamaguchi 338f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 339f0101d0eSyamaguchi lagg_proto_insert_port(psc, pport); 340f0101d0eSyamaguchi 34171d2a73aSyamaguchi LAGG_PROTO_LOCK(psc); 34271d2a73aSyamaguchi pport->lpp_running = true; 34371d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 34471d2a73aSyamaguchi 345f0101d0eSyamaguchi lagg_common_linkstate(psc, lp); 346f0101d0eSyamaguchi } 347f0101d0eSyamaguchi 348f0101d0eSyamaguchi void 349f0101d0eSyamaguchi lagg_common_stopport(struct lagg_proto_softc *psc, struct lagg_port *lp) 350f0101d0eSyamaguchi { 351f0101d0eSyamaguchi struct lagg_proto_port *pport; 352f0101d0eSyamaguchi struct ifnet *ifp; 353f0101d0eSyamaguchi 354f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 35571d2a73aSyamaguchi 35671d2a73aSyamaguchi LAGG_PROTO_LOCK(psc); 35771d2a73aSyamaguchi pport->lpp_running = false; 35871d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 35971d2a73aSyamaguchi 360f0101d0eSyamaguchi lagg_proto_remove_port(psc, pport); 361f0101d0eSyamaguchi 362f0101d0eSyamaguchi if (pport->lpp_active) { 36380873bcaSyamaguchi KASSERT(psc->psc_nactports > 0); 364f0101d0eSyamaguchi psc->psc_nactports--; 365f0101d0eSyamaguchi 366f0101d0eSyamaguchi if (psc->psc_nactports == 0) { 367f0101d0eSyamaguchi ifp = &psc->psc_softc->sc_if; 368f0101d0eSyamaguchi if_link_state_change(ifp, LINK_STATE_DOWN); 369f0101d0eSyamaguchi } 370f0101d0eSyamaguchi 371f0101d0eSyamaguchi pport->lpp_active = false; 372f0101d0eSyamaguchi } 3732beb748dSyamaguchi 3742beb748dSyamaguchi lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); 375f0101d0eSyamaguchi } 376d3cb38e4Syamaguchi static void 377d3cb38e4Syamaguchi lagg_common_linkstate(struct lagg_proto_softc *psc, struct lagg_port *lp) 378d3cb38e4Syamaguchi { 379d3cb38e4Syamaguchi 380d3cb38e4Syamaguchi IFNET_ASSERT_UNLOCKED(lp->lp_ifp); 381d3cb38e4Syamaguchi 382d3cb38e4Syamaguchi IFNET_LOCK(lp->lp_ifp); 383d3cb38e4Syamaguchi lagg_common_linkstate_ifnet_locked(psc, lp); 384d3cb38e4Syamaguchi IFNET_UNLOCK(lp->lp_ifp); 385d3cb38e4Syamaguchi } 386f0101d0eSyamaguchi 387f0101d0eSyamaguchi void 388d3cb38e4Syamaguchi lagg_common_linkstate_ifnet_locked(struct lagg_proto_softc *psc, struct lagg_port *lp) 389f0101d0eSyamaguchi { 390f0101d0eSyamaguchi struct lagg_proto_port *pport; 39171d2a73aSyamaguchi struct ifnet *ifp, *ifp_port; 39271d2a73aSyamaguchi struct ifmediareq ifmr; 39371d2a73aSyamaguchi uint64_t linkspeed; 394f0101d0eSyamaguchi bool is_active; 39571d2a73aSyamaguchi int error; 396f0101d0eSyamaguchi 397f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 398f0101d0eSyamaguchi is_active = lagg_portactive(lp); 39971d2a73aSyamaguchi ifp_port = lp->lp_ifp; 400f0101d0eSyamaguchi 401d3cb38e4Syamaguchi KASSERT(IFNET_LOCKED(ifp_port)); 402d3cb38e4Syamaguchi 40371d2a73aSyamaguchi LAGG_PROTO_LOCK(psc); 40471d2a73aSyamaguchi if (!pport->lpp_running || 40571d2a73aSyamaguchi pport->lpp_active == is_active) { 40671d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 407f0101d0eSyamaguchi return; 40871d2a73aSyamaguchi } 409f0101d0eSyamaguchi 410f0101d0eSyamaguchi ifp = &psc->psc_softc->sc_if; 41171d2a73aSyamaguchi pport->lpp_active = is_active; 41271d2a73aSyamaguchi 413f0101d0eSyamaguchi if (is_active) { 414f0101d0eSyamaguchi psc->psc_nactports++; 415f0101d0eSyamaguchi if (psc->psc_nactports == 1) 416f0101d0eSyamaguchi if_link_state_change(ifp, LINK_STATE_UP); 417f0101d0eSyamaguchi } else { 41880873bcaSyamaguchi KASSERT(psc->psc_nactports > 0); 419f0101d0eSyamaguchi psc->psc_nactports--; 420f0101d0eSyamaguchi 421f0101d0eSyamaguchi if (psc->psc_nactports == 0) 422f0101d0eSyamaguchi if_link_state_change(ifp, LINK_STATE_DOWN); 423f0101d0eSyamaguchi } 42471d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 425f0101d0eSyamaguchi 42671d2a73aSyamaguchi memset(&ifmr, 0, sizeof(ifmr)); 42771d2a73aSyamaguchi error = if_ioctl(ifp_port, SIOCGIFMEDIA, (void *)&ifmr); 42871d2a73aSyamaguchi if (error == 0) { 42971d2a73aSyamaguchi linkspeed = ifmedia_baudrate(ifmr.ifm_active); 43071d2a73aSyamaguchi } else { 43171d2a73aSyamaguchi linkspeed = 0; 43271d2a73aSyamaguchi } 43371d2a73aSyamaguchi 43471d2a73aSyamaguchi LAGG_PROTO_LOCK(psc); 43571d2a73aSyamaguchi pport->lpp_linkspeed = linkspeed; 43671d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 43771d2a73aSyamaguchi lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); 438f0101d0eSyamaguchi } 439f0101d0eSyamaguchi 440f0101d0eSyamaguchi void 441f0101d0eSyamaguchi lagg_common_detach(struct lagg_proto_softc *psc) 442f0101d0eSyamaguchi { 443f0101d0eSyamaguchi 444f0101d0eSyamaguchi lagg_proto_free(psc); 445f0101d0eSyamaguchi } 446f0101d0eSyamaguchi 447f0101d0eSyamaguchi int 448f0101d0eSyamaguchi lagg_none_attach(struct lagg_softc *sc, struct lagg_proto_softc **pscp) 449f0101d0eSyamaguchi { 450f0101d0eSyamaguchi 451f0101d0eSyamaguchi *pscp = NULL; 452f0101d0eSyamaguchi return 0; 453f0101d0eSyamaguchi } 454f0101d0eSyamaguchi 455f0101d0eSyamaguchi int 456f0101d0eSyamaguchi lagg_fail_attach(struct lagg_softc *sc, struct lagg_proto_softc **xpsc) 457f0101d0eSyamaguchi { 458f0101d0eSyamaguchi struct lagg_proto_softc *psc; 459f0101d0eSyamaguchi struct lagg_failover *fovr; 460f0101d0eSyamaguchi 461f0101d0eSyamaguchi psc = lagg_proto_alloc(LAGG_PROTO_FAILOVER, sc); 462f0101d0eSyamaguchi if (psc == NULL) 463f0101d0eSyamaguchi return ENOMEM; 464f0101d0eSyamaguchi 465f0101d0eSyamaguchi fovr = psc->psc_ctx; 466f0101d0eSyamaguchi fovr->fo_rx_all = true; 46771d2a73aSyamaguchi lagg_work_set(&psc->psc_work_linkspeed, 46871d2a73aSyamaguchi lagg_fail_linkspeed_work, psc); 469f0101d0eSyamaguchi 470f0101d0eSyamaguchi *xpsc = psc; 471f0101d0eSyamaguchi return 0; 472f0101d0eSyamaguchi } 473f0101d0eSyamaguchi 474f0101d0eSyamaguchi int 475f0101d0eSyamaguchi lagg_fail_transmit(struct lagg_proto_softc *psc, struct mbuf *m) 476f0101d0eSyamaguchi { 477f0101d0eSyamaguchi struct ifnet *ifp; 478f0101d0eSyamaguchi struct lagg_port *lp; 479f0101d0eSyamaguchi struct psref psref; 480f0101d0eSyamaguchi 481f0101d0eSyamaguchi lp = lagg_link_active(psc, NULL, &psref); 482f0101d0eSyamaguchi if (lp == NULL) { 483f0101d0eSyamaguchi ifp = &psc->psc_softc->sc_if; 484f0101d0eSyamaguchi if_statinc(ifp, if_oerrors); 485f0101d0eSyamaguchi m_freem(m); 486f0101d0eSyamaguchi return ENOENT; 487f0101d0eSyamaguchi } 488f0101d0eSyamaguchi 4895d4d6e29Syamaguchi lagg_output(psc->psc_softc, lp, m); 490f0101d0eSyamaguchi lagg_port_putref(lp, &psref); 491f0101d0eSyamaguchi return 0; 492f0101d0eSyamaguchi } 493f0101d0eSyamaguchi 494f0101d0eSyamaguchi struct mbuf * 495f0101d0eSyamaguchi lagg_fail_input(struct lagg_proto_softc *psc, struct lagg_port *lp, 496f0101d0eSyamaguchi struct mbuf *m) 497f0101d0eSyamaguchi { 498f0101d0eSyamaguchi struct lagg_failover *fovr; 499f0101d0eSyamaguchi struct lagg_port *lp0; 500f0101d0eSyamaguchi struct ifnet *ifp; 501f0101d0eSyamaguchi struct psref psref; 502f0101d0eSyamaguchi 503f0101d0eSyamaguchi fovr = psc->psc_ctx; 504f0101d0eSyamaguchi if (atomic_load_relaxed(&fovr->fo_rx_all)) 505f0101d0eSyamaguchi return m; 506f0101d0eSyamaguchi 507f0101d0eSyamaguchi lp0 = lagg_link_active(psc, NULL, &psref); 508f0101d0eSyamaguchi if (lp0 == NULL) { 509f0101d0eSyamaguchi goto drop; 510f0101d0eSyamaguchi } 511f0101d0eSyamaguchi 512f0101d0eSyamaguchi if (lp0 != lp) { 513f0101d0eSyamaguchi lagg_port_putref(lp0, &psref); 514f0101d0eSyamaguchi goto drop; 515f0101d0eSyamaguchi } 516f0101d0eSyamaguchi 517f0101d0eSyamaguchi lagg_port_putref(lp0, &psref); 518f0101d0eSyamaguchi 519f0101d0eSyamaguchi return m; 520f0101d0eSyamaguchi drop: 521f0101d0eSyamaguchi ifp = &psc->psc_softc->sc_if; 522f0101d0eSyamaguchi if_statinc(ifp, if_ierrors); 523f0101d0eSyamaguchi m_freem(m); 524f0101d0eSyamaguchi return NULL; 525f0101d0eSyamaguchi } 526f0101d0eSyamaguchi 527f0101d0eSyamaguchi void 528f0101d0eSyamaguchi lagg_fail_portstat(struct lagg_proto_softc *psc, struct lagg_port *lp, 529f0101d0eSyamaguchi struct laggreqport *resp) 530f0101d0eSyamaguchi { 531f0101d0eSyamaguchi struct lagg_failover *fovr; 532f0101d0eSyamaguchi struct lagg_proto_port *pport; 533f0101d0eSyamaguchi struct lagg_port *lp0; 534f0101d0eSyamaguchi struct psref psref; 535f0101d0eSyamaguchi 536f0101d0eSyamaguchi fovr = psc->psc_ctx; 537f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 538f0101d0eSyamaguchi 539f0101d0eSyamaguchi if (pport->lpp_active) { 540f0101d0eSyamaguchi lp0 = lagg_link_active(psc, NULL, &psref); 541f0101d0eSyamaguchi if (lp0 == lp) { 542f0101d0eSyamaguchi SET(resp->rp_flags, 5438fa3a4b5Syamaguchi (LAGG_PORT_ACTIVE | 5448fa3a4b5Syamaguchi LAGG_PORT_COLLECTING | 5458fa3a4b5Syamaguchi LAGG_PORT_DISTRIBUTING)); 5468fa3a4b5Syamaguchi } else { 5478fa3a4b5Syamaguchi if (fovr->fo_rx_all) { 5488fa3a4b5Syamaguchi SET(resp->rp_flags, 5498fa3a4b5Syamaguchi LAGG_PORT_COLLECTING); 550f0101d0eSyamaguchi } 5518fa3a4b5Syamaguchi } 5528fa3a4b5Syamaguchi 553f0101d0eSyamaguchi if (lp0 != NULL) 554f0101d0eSyamaguchi lagg_port_putref(lp0, &psref); 555f0101d0eSyamaguchi } 556f0101d0eSyamaguchi } 557f0101d0eSyamaguchi 558f0101d0eSyamaguchi int 559f0101d0eSyamaguchi lagg_fail_ioctl(struct lagg_proto_softc *psc, struct laggreqproto *lreq) 560f0101d0eSyamaguchi { 561f0101d0eSyamaguchi struct lagg_failover *fovr; 562f0101d0eSyamaguchi struct laggreq_fail *rpfail; 563f0101d0eSyamaguchi int error; 564f0101d0eSyamaguchi bool set; 565f0101d0eSyamaguchi 566f0101d0eSyamaguchi error = 0; 567f0101d0eSyamaguchi fovr = psc->psc_ctx; 568f0101d0eSyamaguchi rpfail = &lreq->rp_fail; 569f0101d0eSyamaguchi 570f0101d0eSyamaguchi switch (rpfail->command) { 571f0101d0eSyamaguchi case LAGGIOC_FAILSETFLAGS: 572f0101d0eSyamaguchi case LAGGIOC_FAILCLRFLAGS: 573f0101d0eSyamaguchi set = (rpfail->command == LAGGIOC_FAILSETFLAGS) ? 574f0101d0eSyamaguchi true : false; 575f0101d0eSyamaguchi 576f0101d0eSyamaguchi if (ISSET(rpfail->flags, LAGGREQFAIL_RXALL)) 577f0101d0eSyamaguchi fovr->fo_rx_all = set; 578f0101d0eSyamaguchi break; 579f0101d0eSyamaguchi default: 580f0101d0eSyamaguchi error = ENOTTY; 581f0101d0eSyamaguchi break; 582f0101d0eSyamaguchi } 583f0101d0eSyamaguchi 584f0101d0eSyamaguchi return error; 585f0101d0eSyamaguchi } 586f0101d0eSyamaguchi 58771d2a73aSyamaguchi void 58871d2a73aSyamaguchi lagg_fail_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) 58971d2a73aSyamaguchi { 59071d2a73aSyamaguchi struct lagg_proto_softc *psc = xpsc; 59171d2a73aSyamaguchi struct lagg_proto_port *pport; 59271d2a73aSyamaguchi struct lagg_port *lp; 59371d2a73aSyamaguchi struct psref psref; 59471d2a73aSyamaguchi uint64_t linkspeed; 59571d2a73aSyamaguchi 59671d2a73aSyamaguchi kpreempt_disable(); 59771d2a73aSyamaguchi lp = lagg_link_active(psc, NULL, &psref); 59871d2a73aSyamaguchi if (lp != NULL) { 59971d2a73aSyamaguchi pport = lp->lp_proto_ctx; 60071d2a73aSyamaguchi LAGG_PROTO_LOCK(psc); 60171d2a73aSyamaguchi linkspeed = pport->lpp_linkspeed; 60271d2a73aSyamaguchi LAGG_PROTO_UNLOCK(psc); 60371d2a73aSyamaguchi lagg_port_putref(lp, &psref); 60471d2a73aSyamaguchi } else { 60571d2a73aSyamaguchi linkspeed = 0; 60671d2a73aSyamaguchi } 60771d2a73aSyamaguchi kpreempt_enable(); 60871d2a73aSyamaguchi 60971d2a73aSyamaguchi LAGG_LOCK(psc->psc_softc); 61071d2a73aSyamaguchi lagg_set_linkspeed(psc->psc_softc, linkspeed); 61171d2a73aSyamaguchi LAGG_UNLOCK(psc->psc_softc); 61271d2a73aSyamaguchi } 61371d2a73aSyamaguchi 614f0101d0eSyamaguchi int 615f0101d0eSyamaguchi lagg_lb_attach(struct lagg_softc *sc, struct lagg_proto_softc **xpsc) 616f0101d0eSyamaguchi { 617f0101d0eSyamaguchi struct lagg_proto_softc *psc; 618f0101d0eSyamaguchi struct lagg_lb *lb; 619f0101d0eSyamaguchi 620f0101d0eSyamaguchi psc = lagg_proto_alloc(LAGG_PROTO_LOADBALANCE, sc); 621f0101d0eSyamaguchi if (psc == NULL) 622f0101d0eSyamaguchi return ENOMEM; 623f0101d0eSyamaguchi 624f0101d0eSyamaguchi lb = psc->psc_ctx; 625f0101d0eSyamaguchi lb->lb_pmaps.maps_activepmap = 0; 62671d2a73aSyamaguchi lagg_work_set(&psc->psc_work_linkspeed, 62771d2a73aSyamaguchi lagg_lb_linkspeed_work, psc); 628f0101d0eSyamaguchi 629f0101d0eSyamaguchi *xpsc = psc; 630f0101d0eSyamaguchi return 0; 631f0101d0eSyamaguchi } 632f0101d0eSyamaguchi 633f0101d0eSyamaguchi void 634f0101d0eSyamaguchi lagg_lb_startport(struct lagg_proto_softc *psc, struct lagg_port *lp) 635f0101d0eSyamaguchi { 636f0101d0eSyamaguchi struct lagg_lb *lb; 637f0101d0eSyamaguchi struct lagg_portmap *pm_act, *pm_next; 638f0101d0eSyamaguchi size_t n; 639f0101d0eSyamaguchi 640f0101d0eSyamaguchi lb = psc->psc_ctx; 641f0101d0eSyamaguchi lagg_common_startport(psc, lp); 642f0101d0eSyamaguchi 643f0101d0eSyamaguchi LAGG_PROTO_LOCK(psc); 644f0101d0eSyamaguchi pm_act = lagg_portmap_active(&lb->lb_pmaps); 645f0101d0eSyamaguchi pm_next = lagg_portmap_next(&lb->lb_pmaps); 646f0101d0eSyamaguchi 647f0101d0eSyamaguchi *pm_next = *pm_act; 648f0101d0eSyamaguchi 649f0101d0eSyamaguchi n = pm_next->pm_nports; 650f0101d0eSyamaguchi pm_next->pm_ports[n] = lp; 651f0101d0eSyamaguchi 652f0101d0eSyamaguchi n++; 653f0101d0eSyamaguchi pm_next->pm_nports = n; 654f0101d0eSyamaguchi 655f0101d0eSyamaguchi lagg_portmap_switch(&lb->lb_pmaps); 656f0101d0eSyamaguchi LAGG_PROTO_UNLOCK(psc); 65748a2a44cSyamaguchi pserialize_perform(psc->psc_psz); 658f0101d0eSyamaguchi } 659f0101d0eSyamaguchi 660f0101d0eSyamaguchi void 661f0101d0eSyamaguchi lagg_lb_stopport(struct lagg_proto_softc *psc, struct lagg_port *lp) 662f0101d0eSyamaguchi { 663f0101d0eSyamaguchi struct lagg_lb *lb; 664f0101d0eSyamaguchi struct lagg_portmap *pm_act, *pm_next; 665f0101d0eSyamaguchi size_t i, n; 666f0101d0eSyamaguchi 667f0101d0eSyamaguchi lb = psc->psc_ctx; 668f0101d0eSyamaguchi 669f0101d0eSyamaguchi LAGG_PROTO_LOCK(psc); 670f0101d0eSyamaguchi pm_act = lagg_portmap_active(&lb->lb_pmaps); 671f0101d0eSyamaguchi pm_next = lagg_portmap_next(&lb->lb_pmaps); 672f0101d0eSyamaguchi n = 0; 673f0101d0eSyamaguchi 674f0101d0eSyamaguchi for (i = 0; i < pm_act->pm_nports; i++) { 675f0101d0eSyamaguchi if (pm_act->pm_ports[i] == lp) 676f0101d0eSyamaguchi continue; 677f0101d0eSyamaguchi 678f0101d0eSyamaguchi pm_next->pm_ports[n] = pm_act->pm_ports[i]; 679f0101d0eSyamaguchi n++; 680f0101d0eSyamaguchi } 681f0101d0eSyamaguchi 6829a56fd65Syamaguchi pm_next->pm_nports = n; 6839a56fd65Syamaguchi 684f0101d0eSyamaguchi lagg_portmap_switch(&lb->lb_pmaps); 685f0101d0eSyamaguchi LAGG_PROTO_UNLOCK(psc); 68648a2a44cSyamaguchi pserialize_perform(psc->psc_psz); 687f0101d0eSyamaguchi 688f0101d0eSyamaguchi lagg_common_stopport(psc, lp); 689f0101d0eSyamaguchi } 690f0101d0eSyamaguchi 691f0101d0eSyamaguchi int 692f0101d0eSyamaguchi lagg_lb_transmit(struct lagg_proto_softc *psc, struct mbuf *m) 693f0101d0eSyamaguchi { 694f0101d0eSyamaguchi struct lagg_lb *lb; 695f0101d0eSyamaguchi struct lagg_portmap *pm; 696f0101d0eSyamaguchi struct lagg_port *lp, *lp0; 697f0101d0eSyamaguchi struct ifnet *ifp; 698f0101d0eSyamaguchi struct psref psref; 699f0101d0eSyamaguchi uint32_t hash; 700f0101d0eSyamaguchi int s; 701f0101d0eSyamaguchi 702f0101d0eSyamaguchi lb = psc->psc_ctx; 703f0101d0eSyamaguchi hash = lagg_hashmbuf(psc->psc_softc, m); 704f0101d0eSyamaguchi 705f0101d0eSyamaguchi s = pserialize_read_enter(); 706f0101d0eSyamaguchi 707f0101d0eSyamaguchi pm = lagg_portmap_active(&lb->lb_pmaps); 708e03fa873Syamaguchi if (__predict_true(pm->pm_nports != 0)) { 709f0101d0eSyamaguchi hash %= pm->pm_nports; 710f0101d0eSyamaguchi lp0 = pm->pm_ports[hash]; 711f0101d0eSyamaguchi lp = lagg_link_active(psc, lp0->lp_proto_ctx, &psref); 712e03fa873Syamaguchi } else { 713e03fa873Syamaguchi lp = NULL; 714e03fa873Syamaguchi } 715f0101d0eSyamaguchi 716f0101d0eSyamaguchi pserialize_read_exit(s); 717f0101d0eSyamaguchi 718f0101d0eSyamaguchi if (__predict_false(lp == NULL)) { 719f0101d0eSyamaguchi ifp = &psc->psc_softc->sc_if; 720f0101d0eSyamaguchi if_statinc(ifp, if_oerrors); 721f0101d0eSyamaguchi m_freem(m); 722f0101d0eSyamaguchi return ENOENT; 723f0101d0eSyamaguchi } 724f0101d0eSyamaguchi 7255d4d6e29Syamaguchi lagg_output(psc->psc_softc, lp, m); 726f0101d0eSyamaguchi lagg_port_putref(lp, &psref); 727f0101d0eSyamaguchi 728f0101d0eSyamaguchi return 0; 729f0101d0eSyamaguchi } 730f0101d0eSyamaguchi 731f0101d0eSyamaguchi struct mbuf * 732f0101d0eSyamaguchi lagg_lb_input(struct lagg_proto_softc *psc __unused, 733f0101d0eSyamaguchi struct lagg_port *lp __unused, struct mbuf *m) 734f0101d0eSyamaguchi { 735f0101d0eSyamaguchi 736f0101d0eSyamaguchi return m; 737f0101d0eSyamaguchi } 738f0101d0eSyamaguchi 739f0101d0eSyamaguchi void 740f0101d0eSyamaguchi lagg_lb_portstat(struct lagg_proto_softc *psc, struct lagg_port *lp, 741f0101d0eSyamaguchi struct laggreqport *resp) 742f0101d0eSyamaguchi { 743f0101d0eSyamaguchi struct lagg_proto_port *pport; 744f0101d0eSyamaguchi 745f0101d0eSyamaguchi pport = lp->lp_proto_ctx; 746f0101d0eSyamaguchi 747f0101d0eSyamaguchi if (pport->lpp_active) { 748f0101d0eSyamaguchi SET(resp->rp_flags, LAGG_PORT_ACTIVE | 749f0101d0eSyamaguchi LAGG_PORT_COLLECTING | LAGG_PORT_DISTRIBUTING); 750f0101d0eSyamaguchi } 751f0101d0eSyamaguchi } 75271d2a73aSyamaguchi 75371d2a73aSyamaguchi static void 75471d2a73aSyamaguchi lagg_lb_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) 75571d2a73aSyamaguchi { 75671d2a73aSyamaguchi struct lagg_proto_softc *psc = xpsc; 75771d2a73aSyamaguchi struct lagg_proto_port *pport; 75871d2a73aSyamaguchi uint64_t linkspeed, l; 75971d2a73aSyamaguchi 76071d2a73aSyamaguchi linkspeed = 0; 76171d2a73aSyamaguchi 76287bf012bSyamaguchi LAGG_PROTO_LOCK(psc); /* acquired to refer lpp_linkspeed */ 76371d2a73aSyamaguchi PSLIST_READER_FOREACH(pport, &psc->psc_ports, 76471d2a73aSyamaguchi struct lagg_proto_port, lpp_entry) { 76571d2a73aSyamaguchi if (pport->lpp_active) { 76671d2a73aSyamaguchi l = pport->lpp_linkspeed; 76771d2a73aSyamaguchi linkspeed = MAX(linkspeed, l); 76871d2a73aSyamaguchi } 76971d2a73aSyamaguchi } 77087bf012bSyamaguchi LAGG_PROTO_UNLOCK(psc); 77171d2a73aSyamaguchi 77271d2a73aSyamaguchi LAGG_LOCK(psc->psc_softc); 77371d2a73aSyamaguchi lagg_set_linkspeed(psc->psc_softc, linkspeed); 77471d2a73aSyamaguchi LAGG_UNLOCK(psc->psc_softc); 77571d2a73aSyamaguchi } 776