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