1 1.16 rin /* $NetBSD: if_laggproto.c,v 1.16 2024/09/26 06:08:24 rin Exp $ */ 2 1.1 yamaguch 3 1.1 yamaguch /*- 4 1.1 yamaguch * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5 1.1 yamaguch * 6 1.1 yamaguch * Copyright (c)2021 Internet Initiative Japan, Inc. 7 1.1 yamaguch * All rights reserved. 8 1.1 yamaguch * 9 1.1 yamaguch * Redistribution and use in source and binary forms, with or without 10 1.1 yamaguch * modification, are permitted provided that the following conditions 11 1.1 yamaguch * are met: 12 1.1 yamaguch * 1. Redistributions of source code must retain the above copyright 13 1.1 yamaguch * notice, this list of conditions and the following disclaimer. 14 1.1 yamaguch * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 yamaguch * notice, this list of conditions and the following disclaimer in the 16 1.1 yamaguch * documentation and/or other materials provided with the distribution. 17 1.1 yamaguch * 18 1.1 yamaguch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 1.1 yamaguch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 1.1 yamaguch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 1.1 yamaguch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 1.1 yamaguch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 1.1 yamaguch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 1.1 yamaguch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 1.1 yamaguch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 1.1 yamaguch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 1.1 yamaguch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 1.1 yamaguch * SUCH DAMAGE. 29 1.1 yamaguch */ 30 1.1 yamaguch 31 1.1 yamaguch #include <sys/cdefs.h> 32 1.16 rin __KERNEL_RCSID(0, "$NetBSD: if_laggproto.c,v 1.16 2024/09/26 06:08:24 rin Exp $"); 33 1.1 yamaguch 34 1.1 yamaguch #include <sys/param.h> 35 1.1 yamaguch #include <sys/types.h> 36 1.1 yamaguch 37 1.1 yamaguch #include <sys/evcnt.h> 38 1.1 yamaguch #include <sys/kmem.h> 39 1.1 yamaguch #include <sys/mbuf.h> 40 1.1 yamaguch #include <sys/mutex.h> 41 1.1 yamaguch #include <sys/pslist.h> 42 1.1 yamaguch #include <sys/syslog.h> 43 1.1 yamaguch #include <sys/workqueue.h> 44 1.1 yamaguch 45 1.1 yamaguch #include <net/if.h> 46 1.1 yamaguch #include <net/if_ether.h> 47 1.1 yamaguch #include <net/if_media.h> 48 1.1 yamaguch 49 1.1 yamaguch #include <net/lagg/if_lagg.h> 50 1.1 yamaguch #include <net/lagg/if_laggproto.h> 51 1.1 yamaguch 52 1.1 yamaguch struct lagg_proto_softc { 53 1.1 yamaguch struct lagg_softc *psc_softc; 54 1.1 yamaguch struct pslist_head psc_ports; 55 1.1 yamaguch kmutex_t psc_lock; 56 1.1 yamaguch pserialize_t psc_psz; 57 1.1 yamaguch size_t psc_ctxsiz; 58 1.1 yamaguch void *psc_ctx; 59 1.1 yamaguch size_t psc_nactports; 60 1.7 yamaguch struct workqueue *psc_workq; 61 1.7 yamaguch struct lagg_work psc_work_linkspeed; 62 1.1 yamaguch }; 63 1.1 yamaguch 64 1.1 yamaguch /* 65 1.1 yamaguch * Locking notes: 66 1.1 yamaguch * - Items of struct lagg_proto_softc is protected by 67 1.1 yamaguch * psc_lock (an adaptive mutex) 68 1.12 yamaguch * - psc_ports is protected by pselialize (psc_psz) and 69 1.12 yamaguch * it updates exclusively by LAGG_PROTO_LOCK. 70 1.1 yamaguch * - Other locking notes are described in if_laggproto.h 71 1.1 yamaguch */ 72 1.1 yamaguch 73 1.1 yamaguch struct lagg_failover { 74 1.1 yamaguch bool fo_rx_all; 75 1.1 yamaguch }; 76 1.1 yamaguch 77 1.1 yamaguch struct lagg_portmap { 78 1.1 yamaguch struct lagg_port *pm_ports[LAGG_MAX_PORTS]; 79 1.1 yamaguch size_t pm_nports; 80 1.1 yamaguch }; 81 1.1 yamaguch 82 1.1 yamaguch struct lagg_portmaps { 83 1.1 yamaguch struct lagg_portmap maps_pmap[2]; 84 1.1 yamaguch size_t maps_activepmap; 85 1.1 yamaguch }; 86 1.1 yamaguch 87 1.1 yamaguch struct lagg_lb { 88 1.1 yamaguch struct lagg_portmaps lb_pmaps; 89 1.1 yamaguch }; 90 1.1 yamaguch 91 1.1 yamaguch struct lagg_proto_port { 92 1.1 yamaguch struct pslist_entry lpp_entry; 93 1.1 yamaguch struct lagg_port *lpp_laggport; 94 1.7 yamaguch uint64_t lpp_linkspeed; 95 1.1 yamaguch bool lpp_active; 96 1.7 yamaguch bool lpp_running; 97 1.1 yamaguch }; 98 1.1 yamaguch 99 1.1 yamaguch #define LAGG_PROTO_LOCK(_psc) mutex_enter(&(_psc)->psc_lock) 100 1.1 yamaguch #define LAGG_PROTO_UNLOCK(_psc) mutex_exit(&(_psc)->psc_lock) 101 1.1 yamaguch #define LAGG_PROTO_LOCKED(_psc) mutex_owned(&(_psc)->psc_lock) 102 1.1 yamaguch 103 1.1 yamaguch static struct lagg_proto_softc * 104 1.1 yamaguch lagg_proto_alloc(lagg_proto, struct lagg_softc *); 105 1.1 yamaguch static void lagg_proto_free(struct lagg_proto_softc *); 106 1.1 yamaguch static void lagg_proto_insert_port(struct lagg_proto_softc *, 107 1.1 yamaguch struct lagg_proto_port *); 108 1.1 yamaguch static void lagg_proto_remove_port(struct lagg_proto_softc *, 109 1.1 yamaguch struct lagg_proto_port *); 110 1.1 yamaguch static struct lagg_port * 111 1.1 yamaguch lagg_link_active(struct lagg_proto_softc *psc, 112 1.1 yamaguch struct lagg_proto_port *, struct psref *); 113 1.7 yamaguch static void lagg_fail_linkspeed_work(struct lagg_work *, void *); 114 1.7 yamaguch static void lagg_lb_linkspeed_work(struct lagg_work*, 115 1.7 yamaguch void *); 116 1.8 yamaguch static void lagg_common_linkstate(struct lagg_proto_softc *, 117 1.8 yamaguch struct lagg_port *); 118 1.1 yamaguch 119 1.1 yamaguch static inline struct lagg_portmap * 120 1.1 yamaguch lagg_portmap_active(struct lagg_portmaps *maps) 121 1.1 yamaguch { 122 1.1 yamaguch size_t i; 123 1.1 yamaguch 124 1.1 yamaguch i = atomic_load_consume(&maps->maps_activepmap); 125 1.1 yamaguch 126 1.1 yamaguch return &maps->maps_pmap[i]; 127 1.1 yamaguch } 128 1.1 yamaguch 129 1.1 yamaguch static inline struct lagg_portmap * 130 1.1 yamaguch lagg_portmap_next(struct lagg_portmaps *maps) 131 1.1 yamaguch { 132 1.1 yamaguch size_t i; 133 1.1 yamaguch 134 1.1 yamaguch i = atomic_load_consume(&maps->maps_activepmap); 135 1.1 yamaguch i ^= 0x1; 136 1.1 yamaguch 137 1.1 yamaguch return &maps->maps_pmap[i]; 138 1.1 yamaguch } 139 1.1 yamaguch 140 1.1 yamaguch static inline void 141 1.1 yamaguch lagg_portmap_switch(struct lagg_portmaps *maps) 142 1.1 yamaguch { 143 1.1 yamaguch size_t i; 144 1.1 yamaguch 145 1.1 yamaguch i = atomic_load_consume(&maps->maps_activepmap); 146 1.1 yamaguch i &= 0x1; 147 1.1 yamaguch i ^= 0x1; 148 1.1 yamaguch 149 1.1 yamaguch atomic_store_release(&maps->maps_activepmap, i); 150 1.1 yamaguch } 151 1.1 yamaguch 152 1.1 yamaguch static struct lagg_proto_softc * 153 1.1 yamaguch lagg_proto_alloc(lagg_proto pr, struct lagg_softc *sc) 154 1.1 yamaguch { 155 1.1 yamaguch struct lagg_proto_softc *psc; 156 1.7 yamaguch char xnamebuf[MAXCOMLEN]; 157 1.1 yamaguch size_t ctxsiz; 158 1.1 yamaguch 159 1.1 yamaguch switch (pr) { 160 1.1 yamaguch case LAGG_PROTO_FAILOVER: 161 1.1 yamaguch ctxsiz = sizeof(struct lagg_failover); 162 1.1 yamaguch break; 163 1.1 yamaguch case LAGG_PROTO_LOADBALANCE: 164 1.1 yamaguch ctxsiz = sizeof(struct lagg_lb); 165 1.1 yamaguch break; 166 1.1 yamaguch default: 167 1.1 yamaguch ctxsiz = 0; 168 1.1 yamaguch } 169 1.1 yamaguch 170 1.1 yamaguch psc = kmem_zalloc(sizeof(*psc), KM_NOSLEEP); 171 1.1 yamaguch if (psc == NULL) 172 1.1 yamaguch return NULL; 173 1.1 yamaguch 174 1.16 rin snprintf(xnamebuf, sizeof(xnamebuf), "%s.proto", 175 1.16 rin sc->sc_if.if_xname); 176 1.7 yamaguch psc->psc_workq = lagg_workq_create(xnamebuf, 177 1.7 yamaguch PRI_SOFTNET, IPL_SOFTNET, WQ_MPSAFE); 178 1.7 yamaguch if (psc->psc_workq == NULL) { 179 1.7 yamaguch LAGG_LOG(sc, LOG_ERR, "workqueue create failed\n"); 180 1.7 yamaguch kmem_free(psc, sizeof(*psc)); 181 1.7 yamaguch return NULL; 182 1.7 yamaguch } 183 1.7 yamaguch 184 1.1 yamaguch if (ctxsiz > 0) { 185 1.1 yamaguch psc->psc_ctx = kmem_zalloc(ctxsiz, KM_NOSLEEP); 186 1.1 yamaguch if (psc->psc_ctx == NULL) { 187 1.7 yamaguch lagg_workq_destroy(psc->psc_workq); 188 1.1 yamaguch kmem_free(psc, sizeof(*psc)); 189 1.1 yamaguch return NULL; 190 1.1 yamaguch } 191 1.1 yamaguch 192 1.1 yamaguch psc->psc_ctxsiz = ctxsiz; 193 1.1 yamaguch } 194 1.1 yamaguch 195 1.1 yamaguch PSLIST_INIT(&psc->psc_ports); 196 1.1 yamaguch psc->psc_psz = pserialize_create(); 197 1.1 yamaguch mutex_init(&psc->psc_lock, MUTEX_DEFAULT, IPL_SOFTNET); 198 1.1 yamaguch psc->psc_softc = sc; 199 1.1 yamaguch 200 1.1 yamaguch return psc; 201 1.1 yamaguch } 202 1.1 yamaguch 203 1.1 yamaguch static void 204 1.1 yamaguch lagg_proto_free(struct lagg_proto_softc *psc) 205 1.1 yamaguch { 206 1.1 yamaguch 207 1.7 yamaguch lagg_workq_wait(psc->psc_workq, &psc->psc_work_linkspeed); 208 1.1 yamaguch pserialize_destroy(psc->psc_psz); 209 1.1 yamaguch mutex_destroy(&psc->psc_lock); 210 1.7 yamaguch lagg_workq_destroy(psc->psc_workq); 211 1.15 yamaguch PSLIST_DESTROY(&psc->psc_ports); 212 1.1 yamaguch 213 1.1 yamaguch if (psc->psc_ctxsiz > 0) 214 1.1 yamaguch kmem_free(psc->psc_ctx, psc->psc_ctxsiz); 215 1.1 yamaguch 216 1.1 yamaguch kmem_free(psc, sizeof(*psc)); 217 1.1 yamaguch } 218 1.1 yamaguch 219 1.1 yamaguch static struct lagg_port * 220 1.1 yamaguch lagg_link_active(struct lagg_proto_softc *psc, 221 1.1 yamaguch struct lagg_proto_port *pport, struct psref *psref) 222 1.1 yamaguch { 223 1.1 yamaguch struct lagg_port *lp; 224 1.1 yamaguch int s; 225 1.1 yamaguch 226 1.1 yamaguch lp = NULL; 227 1.1 yamaguch s = pserialize_read_enter(); 228 1.1 yamaguch 229 1.1 yamaguch for (;pport != NULL; 230 1.1 yamaguch pport = PSLIST_READER_NEXT(pport, 231 1.1 yamaguch struct lagg_proto_port, lpp_entry)) { 232 1.1 yamaguch if (atomic_load_relaxed(&pport->lpp_active)) { 233 1.1 yamaguch lp = pport->lpp_laggport; 234 1.1 yamaguch goto done; 235 1.1 yamaguch } 236 1.1 yamaguch } 237 1.1 yamaguch 238 1.1 yamaguch PSLIST_READER_FOREACH(pport, &psc->psc_ports, 239 1.1 yamaguch struct lagg_proto_port, lpp_entry) { 240 1.1 yamaguch if (atomic_load_relaxed(&pport->lpp_active)) { 241 1.1 yamaguch lp = pport->lpp_laggport; 242 1.1 yamaguch break; 243 1.1 yamaguch } 244 1.1 yamaguch } 245 1.1 yamaguch done: 246 1.1 yamaguch if (lp != NULL) 247 1.1 yamaguch lagg_port_getref(lp, psref); 248 1.1 yamaguch pserialize_read_exit(s); 249 1.1 yamaguch 250 1.1 yamaguch return lp; 251 1.1 yamaguch } 252 1.1 yamaguch 253 1.1 yamaguch int 254 1.1 yamaguch lagg_common_allocport(struct lagg_proto_softc *psc, struct lagg_port *lp) 255 1.1 yamaguch { 256 1.1 yamaguch struct lagg_proto_port *pport; 257 1.1 yamaguch 258 1.1 yamaguch KASSERT(LAGG_LOCKED(psc->psc_softc)); 259 1.1 yamaguch 260 1.1 yamaguch pport = kmem_zalloc(sizeof(*pport), KM_NOSLEEP); 261 1.1 yamaguch if (pport == NULL) 262 1.1 yamaguch return ENOMEM; 263 1.1 yamaguch 264 1.1 yamaguch PSLIST_ENTRY_INIT(pport, lpp_entry); 265 1.1 yamaguch pport->lpp_laggport = lp; 266 1.1 yamaguch lp->lp_proto_ctx = (void *)pport; 267 1.1 yamaguch return 0; 268 1.1 yamaguch } 269 1.1 yamaguch 270 1.1 yamaguch void 271 1.1 yamaguch lagg_common_freeport(struct lagg_proto_softc *psc, struct lagg_port *lp) 272 1.1 yamaguch { 273 1.1 yamaguch struct lagg_proto_port *pport; 274 1.1 yamaguch 275 1.1 yamaguch pport = lp->lp_proto_ctx; 276 1.7 yamaguch KASSERT(!pport->lpp_running); 277 1.1 yamaguch lp->lp_proto_ctx = NULL; 278 1.1 yamaguch 279 1.1 yamaguch kmem_free(pport, sizeof(*pport)); 280 1.1 yamaguch } 281 1.1 yamaguch 282 1.1 yamaguch static void 283 1.1 yamaguch lagg_proto_insert_port(struct lagg_proto_softc *psc, 284 1.1 yamaguch struct lagg_proto_port *pport) 285 1.1 yamaguch { 286 1.1 yamaguch struct lagg_proto_port *pport0; 287 1.1 yamaguch struct lagg_port *lp, *lp0; 288 1.1 yamaguch bool insert_after; 289 1.1 yamaguch 290 1.1 yamaguch insert_after = false; 291 1.1 yamaguch lp = pport->lpp_laggport; 292 1.1 yamaguch 293 1.1 yamaguch LAGG_PROTO_LOCK(psc); 294 1.1 yamaguch PSLIST_WRITER_FOREACH(pport0, &psc->psc_ports, 295 1.1 yamaguch struct lagg_proto_port, lpp_entry) { 296 1.1 yamaguch lp0 = pport0->lpp_laggport; 297 1.1 yamaguch if (lp0->lp_prio > lp->lp_prio) 298 1.1 yamaguch break; 299 1.1 yamaguch 300 1.1 yamaguch if (PSLIST_WRITER_NEXT(pport0, 301 1.1 yamaguch struct lagg_proto_port, lpp_entry) == NULL) { 302 1.1 yamaguch insert_after = true; 303 1.1 yamaguch break; 304 1.1 yamaguch } 305 1.1 yamaguch } 306 1.1 yamaguch 307 1.1 yamaguch if (pport0 == NULL) { 308 1.1 yamaguch PSLIST_WRITER_INSERT_HEAD(&psc->psc_ports, pport, 309 1.1 yamaguch lpp_entry); 310 1.1 yamaguch } else if (insert_after) { 311 1.1 yamaguch PSLIST_WRITER_INSERT_AFTER(pport0, pport, lpp_entry); 312 1.1 yamaguch } else { 313 1.1 yamaguch PSLIST_WRITER_INSERT_BEFORE(pport0, pport, lpp_entry); 314 1.1 yamaguch } 315 1.1 yamaguch LAGG_PROTO_UNLOCK(psc); 316 1.1 yamaguch } 317 1.1 yamaguch 318 1.1 yamaguch static void 319 1.1 yamaguch lagg_proto_remove_port(struct lagg_proto_softc *psc, 320 1.1 yamaguch struct lagg_proto_port *pport) 321 1.1 yamaguch { 322 1.1 yamaguch 323 1.1 yamaguch LAGG_PROTO_LOCK(psc); 324 1.1 yamaguch PSLIST_WRITER_REMOVE(pport, lpp_entry); 325 1.14 yamaguch LAGG_PROTO_UNLOCK(psc); 326 1.1 yamaguch pserialize_perform(psc->psc_psz); 327 1.15 yamaguch 328 1.15 yamaguch /* re-initialize for reuse */ 329 1.15 yamaguch PSLIST_ENTRY_DESTROY(pport, lpp_entry); 330 1.15 yamaguch PSLIST_ENTRY_INIT(pport, lpp_entry); 331 1.1 yamaguch } 332 1.1 yamaguch 333 1.1 yamaguch void 334 1.1 yamaguch lagg_common_startport(struct lagg_proto_softc *psc, struct lagg_port *lp) 335 1.1 yamaguch { 336 1.1 yamaguch struct lagg_proto_port *pport; 337 1.1 yamaguch 338 1.1 yamaguch pport = lp->lp_proto_ctx; 339 1.1 yamaguch lagg_proto_insert_port(psc, pport); 340 1.1 yamaguch 341 1.7 yamaguch LAGG_PROTO_LOCK(psc); 342 1.7 yamaguch pport->lpp_running = true; 343 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 344 1.7 yamaguch 345 1.1 yamaguch lagg_common_linkstate(psc, lp); 346 1.1 yamaguch } 347 1.1 yamaguch 348 1.1 yamaguch void 349 1.1 yamaguch lagg_common_stopport(struct lagg_proto_softc *psc, struct lagg_port *lp) 350 1.1 yamaguch { 351 1.1 yamaguch struct lagg_proto_port *pport; 352 1.1 yamaguch struct ifnet *ifp; 353 1.1 yamaguch 354 1.1 yamaguch pport = lp->lp_proto_ctx; 355 1.7 yamaguch 356 1.7 yamaguch LAGG_PROTO_LOCK(psc); 357 1.7 yamaguch pport->lpp_running = false; 358 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 359 1.7 yamaguch 360 1.1 yamaguch lagg_proto_remove_port(psc, pport); 361 1.1 yamaguch 362 1.1 yamaguch if (pport->lpp_active) { 363 1.3 yamaguch KASSERT(psc->psc_nactports > 0); 364 1.3 yamaguch psc->psc_nactports--; 365 1.1 yamaguch 366 1.1 yamaguch if (psc->psc_nactports == 0) { 367 1.1 yamaguch ifp = &psc->psc_softc->sc_if; 368 1.1 yamaguch if_link_state_change(ifp, LINK_STATE_DOWN); 369 1.1 yamaguch } 370 1.1 yamaguch 371 1.1 yamaguch pport->lpp_active = false; 372 1.1 yamaguch } 373 1.9 yamaguch 374 1.9 yamaguch lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); 375 1.1 yamaguch } 376 1.8 yamaguch static void 377 1.8 yamaguch lagg_common_linkstate(struct lagg_proto_softc *psc, struct lagg_port *lp) 378 1.8 yamaguch { 379 1.8 yamaguch 380 1.8 yamaguch IFNET_ASSERT_UNLOCKED(lp->lp_ifp); 381 1.8 yamaguch 382 1.8 yamaguch IFNET_LOCK(lp->lp_ifp); 383 1.8 yamaguch lagg_common_linkstate_ifnet_locked(psc, lp); 384 1.8 yamaguch IFNET_UNLOCK(lp->lp_ifp); 385 1.8 yamaguch } 386 1.1 yamaguch 387 1.1 yamaguch void 388 1.8 yamaguch lagg_common_linkstate_ifnet_locked(struct lagg_proto_softc *psc, struct lagg_port *lp) 389 1.1 yamaguch { 390 1.1 yamaguch struct lagg_proto_port *pport; 391 1.7 yamaguch struct ifnet *ifp, *ifp_port; 392 1.7 yamaguch struct ifmediareq ifmr; 393 1.7 yamaguch uint64_t linkspeed; 394 1.1 yamaguch bool is_active; 395 1.7 yamaguch int error; 396 1.1 yamaguch 397 1.1 yamaguch pport = lp->lp_proto_ctx; 398 1.1 yamaguch is_active = lagg_portactive(lp); 399 1.7 yamaguch ifp_port = lp->lp_ifp; 400 1.1 yamaguch 401 1.8 yamaguch KASSERT(IFNET_LOCKED(ifp_port)); 402 1.8 yamaguch 403 1.7 yamaguch LAGG_PROTO_LOCK(psc); 404 1.7 yamaguch if (!pport->lpp_running || 405 1.7 yamaguch pport->lpp_active == is_active) { 406 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 407 1.1 yamaguch return; 408 1.7 yamaguch } 409 1.1 yamaguch 410 1.1 yamaguch ifp = &psc->psc_softc->sc_if; 411 1.7 yamaguch pport->lpp_active = is_active; 412 1.7 yamaguch 413 1.1 yamaguch if (is_active) { 414 1.1 yamaguch psc->psc_nactports++; 415 1.1 yamaguch if (psc->psc_nactports == 1) 416 1.1 yamaguch if_link_state_change(ifp, LINK_STATE_UP); 417 1.1 yamaguch } else { 418 1.3 yamaguch KASSERT(psc->psc_nactports > 0); 419 1.3 yamaguch psc->psc_nactports--; 420 1.1 yamaguch 421 1.1 yamaguch if (psc->psc_nactports == 0) 422 1.1 yamaguch if_link_state_change(ifp, LINK_STATE_DOWN); 423 1.1 yamaguch } 424 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 425 1.7 yamaguch 426 1.7 yamaguch memset(&ifmr, 0, sizeof(ifmr)); 427 1.7 yamaguch error = if_ioctl(ifp_port, SIOCGIFMEDIA, (void *)&ifmr); 428 1.7 yamaguch if (error == 0) { 429 1.7 yamaguch linkspeed = ifmedia_baudrate(ifmr.ifm_active); 430 1.7 yamaguch } else { 431 1.7 yamaguch linkspeed = 0; 432 1.7 yamaguch } 433 1.1 yamaguch 434 1.7 yamaguch LAGG_PROTO_LOCK(psc); 435 1.7 yamaguch pport->lpp_linkspeed = linkspeed; 436 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 437 1.7 yamaguch lagg_workq_add(psc->psc_workq, &psc->psc_work_linkspeed); 438 1.1 yamaguch } 439 1.1 yamaguch 440 1.1 yamaguch void 441 1.1 yamaguch lagg_common_detach(struct lagg_proto_softc *psc) 442 1.1 yamaguch { 443 1.1 yamaguch 444 1.1 yamaguch lagg_proto_free(psc); 445 1.1 yamaguch } 446 1.1 yamaguch 447 1.1 yamaguch int 448 1.1 yamaguch lagg_none_attach(struct lagg_softc *sc, struct lagg_proto_softc **pscp) 449 1.1 yamaguch { 450 1.1 yamaguch 451 1.1 yamaguch *pscp = NULL; 452 1.1 yamaguch return 0; 453 1.1 yamaguch } 454 1.1 yamaguch 455 1.1 yamaguch int 456 1.1 yamaguch lagg_fail_attach(struct lagg_softc *sc, struct lagg_proto_softc **xpsc) 457 1.1 yamaguch { 458 1.1 yamaguch struct lagg_proto_softc *psc; 459 1.1 yamaguch struct lagg_failover *fovr; 460 1.1 yamaguch 461 1.1 yamaguch psc = lagg_proto_alloc(LAGG_PROTO_FAILOVER, sc); 462 1.1 yamaguch if (psc == NULL) 463 1.1 yamaguch return ENOMEM; 464 1.1 yamaguch 465 1.1 yamaguch fovr = psc->psc_ctx; 466 1.1 yamaguch fovr->fo_rx_all = true; 467 1.7 yamaguch lagg_work_set(&psc->psc_work_linkspeed, 468 1.7 yamaguch lagg_fail_linkspeed_work, psc); 469 1.1 yamaguch 470 1.1 yamaguch *xpsc = psc; 471 1.1 yamaguch return 0; 472 1.1 yamaguch } 473 1.1 yamaguch 474 1.1 yamaguch int 475 1.1 yamaguch lagg_fail_transmit(struct lagg_proto_softc *psc, struct mbuf *m) 476 1.1 yamaguch { 477 1.1 yamaguch struct ifnet *ifp; 478 1.1 yamaguch struct lagg_port *lp; 479 1.1 yamaguch struct psref psref; 480 1.1 yamaguch 481 1.1 yamaguch lp = lagg_link_active(psc, NULL, &psref); 482 1.1 yamaguch if (lp == NULL) { 483 1.1 yamaguch ifp = &psc->psc_softc->sc_if; 484 1.1 yamaguch if_statinc(ifp, if_oerrors); 485 1.1 yamaguch m_freem(m); 486 1.1 yamaguch return ENOENT; 487 1.1 yamaguch } 488 1.1 yamaguch 489 1.6 yamaguch lagg_output(psc->psc_softc, lp, m); 490 1.1 yamaguch lagg_port_putref(lp, &psref); 491 1.1 yamaguch return 0; 492 1.1 yamaguch } 493 1.1 yamaguch 494 1.1 yamaguch struct mbuf * 495 1.1 yamaguch lagg_fail_input(struct lagg_proto_softc *psc, struct lagg_port *lp, 496 1.1 yamaguch struct mbuf *m) 497 1.1 yamaguch { 498 1.1 yamaguch struct lagg_failover *fovr; 499 1.1 yamaguch struct lagg_port *lp0; 500 1.1 yamaguch struct ifnet *ifp; 501 1.1 yamaguch struct psref psref; 502 1.1 yamaguch 503 1.1 yamaguch fovr = psc->psc_ctx; 504 1.1 yamaguch if (atomic_load_relaxed(&fovr->fo_rx_all)) 505 1.1 yamaguch return m; 506 1.1 yamaguch 507 1.1 yamaguch lp0 = lagg_link_active(psc, NULL, &psref); 508 1.1 yamaguch if (lp0 == NULL) { 509 1.1 yamaguch goto drop; 510 1.1 yamaguch } 511 1.1 yamaguch 512 1.1 yamaguch if (lp0 != lp) { 513 1.1 yamaguch lagg_port_putref(lp0, &psref); 514 1.1 yamaguch goto drop; 515 1.1 yamaguch } 516 1.1 yamaguch 517 1.1 yamaguch lagg_port_putref(lp0, &psref); 518 1.1 yamaguch 519 1.1 yamaguch return m; 520 1.1 yamaguch drop: 521 1.1 yamaguch ifp = &psc->psc_softc->sc_if; 522 1.1 yamaguch if_statinc(ifp, if_ierrors); 523 1.1 yamaguch m_freem(m); 524 1.1 yamaguch return NULL; 525 1.1 yamaguch } 526 1.1 yamaguch 527 1.1 yamaguch void 528 1.1 yamaguch lagg_fail_portstat(struct lagg_proto_softc *psc, struct lagg_port *lp, 529 1.1 yamaguch struct laggreqport *resp) 530 1.1 yamaguch { 531 1.1 yamaguch struct lagg_failover *fovr; 532 1.1 yamaguch struct lagg_proto_port *pport; 533 1.1 yamaguch struct lagg_port *lp0; 534 1.1 yamaguch struct psref psref; 535 1.1 yamaguch 536 1.1 yamaguch fovr = psc->psc_ctx; 537 1.1 yamaguch pport = lp->lp_proto_ctx; 538 1.1 yamaguch 539 1.1 yamaguch if (pport->lpp_active) { 540 1.1 yamaguch lp0 = lagg_link_active(psc, NULL, &psref); 541 1.1 yamaguch if (lp0 == lp) { 542 1.1 yamaguch SET(resp->rp_flags, 543 1.4 yamaguch (LAGG_PORT_ACTIVE | 544 1.4 yamaguch LAGG_PORT_COLLECTING | 545 1.4 yamaguch LAGG_PORT_DISTRIBUTING)); 546 1.4 yamaguch } else { 547 1.4 yamaguch if (fovr->fo_rx_all) { 548 1.4 yamaguch SET(resp->rp_flags, 549 1.4 yamaguch LAGG_PORT_COLLECTING); 550 1.4 yamaguch } 551 1.1 yamaguch } 552 1.4 yamaguch 553 1.1 yamaguch if (lp0 != NULL) 554 1.1 yamaguch lagg_port_putref(lp0, &psref); 555 1.1 yamaguch } 556 1.1 yamaguch } 557 1.1 yamaguch 558 1.1 yamaguch int 559 1.1 yamaguch lagg_fail_ioctl(struct lagg_proto_softc *psc, struct laggreqproto *lreq) 560 1.1 yamaguch { 561 1.1 yamaguch struct lagg_failover *fovr; 562 1.1 yamaguch struct laggreq_fail *rpfail; 563 1.1 yamaguch int error; 564 1.1 yamaguch bool set; 565 1.1 yamaguch 566 1.1 yamaguch error = 0; 567 1.1 yamaguch fovr = psc->psc_ctx; 568 1.1 yamaguch rpfail = &lreq->rp_fail; 569 1.1 yamaguch 570 1.1 yamaguch switch (rpfail->command) { 571 1.1 yamaguch case LAGGIOC_FAILSETFLAGS: 572 1.1 yamaguch case LAGGIOC_FAILCLRFLAGS: 573 1.1 yamaguch set = (rpfail->command == LAGGIOC_FAILSETFLAGS) ? 574 1.1 yamaguch true : false; 575 1.1 yamaguch 576 1.1 yamaguch if (ISSET(rpfail->flags, LAGGREQFAIL_RXALL)) 577 1.1 yamaguch fovr->fo_rx_all = set; 578 1.1 yamaguch break; 579 1.1 yamaguch default: 580 1.1 yamaguch error = ENOTTY; 581 1.1 yamaguch break; 582 1.1 yamaguch } 583 1.1 yamaguch 584 1.1 yamaguch return error; 585 1.1 yamaguch } 586 1.1 yamaguch 587 1.7 yamaguch void 588 1.7 yamaguch lagg_fail_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) 589 1.7 yamaguch { 590 1.7 yamaguch struct lagg_proto_softc *psc = xpsc; 591 1.7 yamaguch struct lagg_proto_port *pport; 592 1.7 yamaguch struct lagg_port *lp; 593 1.7 yamaguch struct psref psref; 594 1.7 yamaguch uint64_t linkspeed; 595 1.7 yamaguch 596 1.7 yamaguch kpreempt_disable(); 597 1.7 yamaguch lp = lagg_link_active(psc, NULL, &psref); 598 1.7 yamaguch if (lp != NULL) { 599 1.7 yamaguch pport = lp->lp_proto_ctx; 600 1.7 yamaguch LAGG_PROTO_LOCK(psc); 601 1.7 yamaguch linkspeed = pport->lpp_linkspeed; 602 1.7 yamaguch LAGG_PROTO_UNLOCK(psc); 603 1.7 yamaguch lagg_port_putref(lp, &psref); 604 1.7 yamaguch } else { 605 1.7 yamaguch linkspeed = 0; 606 1.7 yamaguch } 607 1.7 yamaguch kpreempt_enable(); 608 1.7 yamaguch 609 1.7 yamaguch LAGG_LOCK(psc->psc_softc); 610 1.7 yamaguch lagg_set_linkspeed(psc->psc_softc, linkspeed); 611 1.7 yamaguch LAGG_UNLOCK(psc->psc_softc); 612 1.7 yamaguch } 613 1.7 yamaguch 614 1.1 yamaguch int 615 1.1 yamaguch lagg_lb_attach(struct lagg_softc *sc, struct lagg_proto_softc **xpsc) 616 1.1 yamaguch { 617 1.1 yamaguch struct lagg_proto_softc *psc; 618 1.1 yamaguch struct lagg_lb *lb; 619 1.1 yamaguch 620 1.1 yamaguch psc = lagg_proto_alloc(LAGG_PROTO_LOADBALANCE, sc); 621 1.1 yamaguch if (psc == NULL) 622 1.1 yamaguch return ENOMEM; 623 1.1 yamaguch 624 1.1 yamaguch lb = psc->psc_ctx; 625 1.1 yamaguch lb->lb_pmaps.maps_activepmap = 0; 626 1.7 yamaguch lagg_work_set(&psc->psc_work_linkspeed, 627 1.7 yamaguch lagg_lb_linkspeed_work, psc); 628 1.1 yamaguch 629 1.1 yamaguch *xpsc = psc; 630 1.1 yamaguch return 0; 631 1.1 yamaguch } 632 1.1 yamaguch 633 1.1 yamaguch void 634 1.1 yamaguch lagg_lb_startport(struct lagg_proto_softc *psc, struct lagg_port *lp) 635 1.1 yamaguch { 636 1.1 yamaguch struct lagg_lb *lb; 637 1.1 yamaguch struct lagg_portmap *pm_act, *pm_next; 638 1.1 yamaguch size_t n; 639 1.1 yamaguch 640 1.1 yamaguch lb = psc->psc_ctx; 641 1.1 yamaguch lagg_common_startport(psc, lp); 642 1.1 yamaguch 643 1.1 yamaguch LAGG_PROTO_LOCK(psc); 644 1.1 yamaguch pm_act = lagg_portmap_active(&lb->lb_pmaps); 645 1.1 yamaguch pm_next = lagg_portmap_next(&lb->lb_pmaps); 646 1.1 yamaguch 647 1.1 yamaguch *pm_next = *pm_act; 648 1.1 yamaguch 649 1.1 yamaguch n = pm_next->pm_nports; 650 1.1 yamaguch pm_next->pm_ports[n] = lp; 651 1.1 yamaguch 652 1.1 yamaguch n++; 653 1.1 yamaguch pm_next->pm_nports = n; 654 1.1 yamaguch 655 1.1 yamaguch lagg_portmap_switch(&lb->lb_pmaps); 656 1.14 yamaguch LAGG_PROTO_UNLOCK(psc); 657 1.1 yamaguch pserialize_perform(psc->psc_psz); 658 1.1 yamaguch } 659 1.1 yamaguch 660 1.1 yamaguch void 661 1.1 yamaguch lagg_lb_stopport(struct lagg_proto_softc *psc, struct lagg_port *lp) 662 1.1 yamaguch { 663 1.1 yamaguch struct lagg_lb *lb; 664 1.1 yamaguch struct lagg_portmap *pm_act, *pm_next; 665 1.1 yamaguch size_t i, n; 666 1.1 yamaguch 667 1.1 yamaguch lb = psc->psc_ctx; 668 1.1 yamaguch 669 1.1 yamaguch LAGG_PROTO_LOCK(psc); 670 1.1 yamaguch pm_act = lagg_portmap_active(&lb->lb_pmaps); 671 1.1 yamaguch pm_next = lagg_portmap_next(&lb->lb_pmaps); 672 1.1 yamaguch n = 0; 673 1.1 yamaguch 674 1.1 yamaguch for (i = 0; i < pm_act->pm_nports; i++) { 675 1.1 yamaguch if (pm_act->pm_ports[i] == lp) 676 1.1 yamaguch continue; 677 1.1 yamaguch 678 1.1 yamaguch pm_next->pm_ports[n] = pm_act->pm_ports[i]; 679 1.1 yamaguch n++; 680 1.1 yamaguch } 681 1.1 yamaguch 682 1.10 yamaguch pm_next->pm_nports = n; 683 1.10 yamaguch 684 1.1 yamaguch lagg_portmap_switch(&lb->lb_pmaps); 685 1.14 yamaguch LAGG_PROTO_UNLOCK(psc); 686 1.1 yamaguch pserialize_perform(psc->psc_psz); 687 1.1 yamaguch 688 1.1 yamaguch lagg_common_stopport(psc, lp); 689 1.1 yamaguch } 690 1.1 yamaguch 691 1.1 yamaguch int 692 1.1 yamaguch lagg_lb_transmit(struct lagg_proto_softc *psc, struct mbuf *m) 693 1.1 yamaguch { 694 1.1 yamaguch struct lagg_lb *lb; 695 1.1 yamaguch struct lagg_portmap *pm; 696 1.1 yamaguch struct lagg_port *lp, *lp0; 697 1.1 yamaguch struct ifnet *ifp; 698 1.1 yamaguch struct psref psref; 699 1.1 yamaguch uint32_t hash; 700 1.1 yamaguch int s; 701 1.1 yamaguch 702 1.1 yamaguch lb = psc->psc_ctx; 703 1.11 yamaguch hash = lagg_hashmbuf(psc->psc_softc, m); 704 1.1 yamaguch 705 1.1 yamaguch s = pserialize_read_enter(); 706 1.1 yamaguch 707 1.1 yamaguch pm = lagg_portmap_active(&lb->lb_pmaps); 708 1.11 yamaguch if (__predict_true(pm->pm_nports != 0)) { 709 1.11 yamaguch hash %= pm->pm_nports; 710 1.11 yamaguch lp0 = pm->pm_ports[hash]; 711 1.11 yamaguch lp = lagg_link_active(psc, lp0->lp_proto_ctx, &psref); 712 1.11 yamaguch } else { 713 1.11 yamaguch lp = NULL; 714 1.11 yamaguch } 715 1.1 yamaguch 716 1.1 yamaguch pserialize_read_exit(s); 717 1.1 yamaguch 718 1.1 yamaguch if (__predict_false(lp == NULL)) { 719 1.1 yamaguch ifp = &psc->psc_softc->sc_if; 720 1.1 yamaguch if_statinc(ifp, if_oerrors); 721 1.1 yamaguch m_freem(m); 722 1.1 yamaguch return ENOENT; 723 1.1 yamaguch } 724 1.1 yamaguch 725 1.6 yamaguch lagg_output(psc->psc_softc, lp, m); 726 1.1 yamaguch lagg_port_putref(lp, &psref); 727 1.1 yamaguch 728 1.1 yamaguch return 0; 729 1.1 yamaguch } 730 1.1 yamaguch 731 1.1 yamaguch struct mbuf * 732 1.1 yamaguch lagg_lb_input(struct lagg_proto_softc *psc __unused, 733 1.1 yamaguch struct lagg_port *lp __unused, struct mbuf *m) 734 1.1 yamaguch { 735 1.1 yamaguch 736 1.1 yamaguch return m; 737 1.1 yamaguch } 738 1.1 yamaguch 739 1.1 yamaguch void 740 1.1 yamaguch lagg_lb_portstat(struct lagg_proto_softc *psc, struct lagg_port *lp, 741 1.1 yamaguch struct laggreqport *resp) 742 1.1 yamaguch { 743 1.1 yamaguch struct lagg_proto_port *pport; 744 1.1 yamaguch 745 1.1 yamaguch pport = lp->lp_proto_ctx; 746 1.1 yamaguch 747 1.1 yamaguch if (pport->lpp_active) { 748 1.1 yamaguch SET(resp->rp_flags, LAGG_PORT_ACTIVE | 749 1.1 yamaguch LAGG_PORT_COLLECTING | LAGG_PORT_DISTRIBUTING); 750 1.1 yamaguch } 751 1.1 yamaguch } 752 1.7 yamaguch 753 1.7 yamaguch static void 754 1.7 yamaguch lagg_lb_linkspeed_work(struct lagg_work *_lw __unused, void *xpsc) 755 1.7 yamaguch { 756 1.7 yamaguch struct lagg_proto_softc *psc = xpsc; 757 1.7 yamaguch struct lagg_proto_port *pport; 758 1.7 yamaguch uint64_t linkspeed, l; 759 1.7 yamaguch 760 1.7 yamaguch linkspeed = 0; 761 1.7 yamaguch 762 1.12 yamaguch LAGG_PROTO_LOCK(psc); /* acquired to refer lpp_linkspeed */ 763 1.7 yamaguch PSLIST_READER_FOREACH(pport, &psc->psc_ports, 764 1.7 yamaguch struct lagg_proto_port, lpp_entry) { 765 1.7 yamaguch if (pport->lpp_active) { 766 1.7 yamaguch l = pport->lpp_linkspeed; 767 1.7 yamaguch linkspeed = MAX(linkspeed, l); 768 1.7 yamaguch } 769 1.7 yamaguch } 770 1.12 yamaguch LAGG_PROTO_UNLOCK(psc); 771 1.7 yamaguch 772 1.7 yamaguch LAGG_LOCK(psc->psc_softc); 773 1.7 yamaguch lagg_set_linkspeed(psc->psc_softc, linkspeed); 774 1.7 yamaguch LAGG_UNLOCK(psc->psc_softc); 775 1.7 yamaguch } 776