Module Name:    src
Committed By:   yamaguchi
Date:           Wed Nov 22 03:49:13 UTC 2023

Modified Files:
        src/sys/net/lagg: if_lagg.c if_lagg_lacp.c if_lagg_lacp.h
            if_laggproto.c if_laggproto.h

Log Message:
Set the fastest linkspeed in each physical interface to lagg(4)


To generate a diff of this commit:
cvs rdiff -u -r1.53 -r1.54 src/sys/net/lagg/if_lagg.c
cvs rdiff -u -r1.27 -r1.28 src/sys/net/lagg/if_lagg_lacp.c
cvs rdiff -u -r1.4 -r1.5 src/sys/net/lagg/if_lagg_lacp.h
cvs rdiff -u -r1.6 -r1.7 src/sys/net/lagg/if_laggproto.c
cvs rdiff -u -r1.18 -r1.19 src/sys/net/lagg/if_laggproto.h

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.53 src/sys/net/lagg/if_lagg.c:1.54
--- src/sys/net/lagg/if_lagg.c:1.53	Wed Nov 22 03:30:57 2023
+++ src/sys/net/lagg/if_lagg.c	Wed Nov 22 03:49:13 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_lagg.c,v 1.53 2023/11/22 03:30:57 yamaguchi Exp $	*/
+/*	$NetBSD: if_lagg.c,v 1.54 2023/11/22 03:49:13 yamaguchi 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.53 2023/11/22 03:30:57 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_lagg.c,v 1.54 2023/11/22 03:49:13 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1217,6 +1217,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;
@@ -1224,6 +1227,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)
@@ -1630,6 +1679,8 @@ lagg_pr_attach(struct lagg_softc *sc, la
 		lagg_proto_detach(oldvar);
 		cleanup_oldvar = true;
 	}
+
+	lagg_set_linkspeed(sc, 0);
 done:
 	LAGG_UNLOCK(sc);
 

Index: src/sys/net/lagg/if_lagg_lacp.c
diff -u src/sys/net/lagg/if_lagg_lacp.c:1.27 src/sys/net/lagg/if_lagg_lacp.c:1.28
--- src/sys/net/lagg/if_lagg_lacp.c:1.27	Wed Nov 22 03:27:00 2023
+++ src/sys/net/lagg/if_lagg_lacp.c	Wed Nov 22 03:49:13 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_lagg_lacp.c,v 1.27 2023/11/22 03:27:00 yamaguchi Exp $	*/
+/*	$NetBSD: if_lagg_lacp.c,v 1.28 2023/11/22 03:49:13 yamaguchi 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.27 2023/11/22 03:27:00 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_lagg_lacp.c,v 1.28 2023/11/22 03:49:13 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_lagg.h"
@@ -126,7 +126,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 +165,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 +248,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 +285,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 *);
@@ -413,9 +414,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 +488,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);
@@ -852,7 +855,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 +864,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 +889,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 +922,6 @@ lacp_linkstate_ifnet_locked(struct lagg_
 		LACP_DPRINTF((lsc, lacpp,
 		    "LACP is inactive, skip linkstate\n"));
 	}
-
 	LACP_UNLOCK(lsc);
 }
 
@@ -2051,6 +2060,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));
@@ -2355,7 +2366,7 @@ lacp_selected_update(struct lacp_softc *
 {
 	struct lacp_port *lacpp;
 	size_t nselected;
-	uint32_t media;
+	uint64_t linkspeed;
 
 	KASSERT(LACP_LOCKED(lsc));
 
@@ -2363,14 +2374,13 @@ 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)) {
+		    (!lsc->lsc_multi_linkspeed && linkspeed != lacpp->lp_linkspeed)) {
 			if (lacpp->lp_selected == LACP_SELECTED)
 				lacpp->lp_selected = LACP_STANDBY;
-			continue;
 		}
 
 		switch (lacpp->lp_selected) {
@@ -2677,62 +2687,6 @@ 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;
-}
-
 static void
 lacp_linkstate(struct lagg_proto_softc *xlsc, struct lagg_port *lp)
 {
@@ -2743,3 +2697,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.5
--- 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	Wed Nov 22 03:49:13 2023
@@ -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.5 2023/11/22 03:49:13 yamaguchi 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.7
--- src/sys/net/lagg/if_laggproto.c:1.6	Thu Mar 31 07:59:05 2022
+++ src/sys/net/lagg/if_laggproto.c	Wed Nov 22 03:49:13 2023
@@ -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.7 2023/11/22 03:49:13 yamaguchi 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.7 2023/11/22 03:49:13 yamaguchi Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -57,6 +57,8 @@ 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;
 };
 
 /*
@@ -90,7 +92,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 +111,9 @@ 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 inline struct lagg_portmap *
 lagg_portmap_active(struct lagg_portmaps *maps)
@@ -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,18 @@ lagg_proto_alloc(lagg_proto pr, struct l
 	if (psc == NULL)
 		return NULL;
 
+	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 +202,10 @@ 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);
 
 	if (psc->psc_ctxsiz > 0)
 		kmem_free(psc->psc_ctx, psc->psc_ctxsiz);
@@ -251,6 +270,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));
@@ -311,6 +331,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 +345,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) {
@@ -340,16 +369,26 @@ void
 lagg_common_linkstate(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;
 
-	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 +400,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 +443,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 +563,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 +602,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;
@@ -640,3 +722,30 @@ 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;
+	int s;
+
+	linkspeed = 0;
+
+	s = pserialize_read_enter();
+	PSLIST_READER_FOREACH(pport, &psc->psc_ports,
+	    struct lagg_proto_port, lpp_entry) {
+		if (pport->lpp_active) {
+			LAGG_PROTO_LOCK(psc);
+			l = pport->lpp_linkspeed;
+			LAGG_PROTO_UNLOCK(psc);
+			linkspeed = MAX(linkspeed, l);
+		}
+	}
+	pserialize_read_exit(s);
+
+	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.19
--- src/sys/net/lagg/if_laggproto.h:1.18	Sun Jun 26 17:55:24 2022
+++ src/sys/net/lagg/if_laggproto.h	Wed Nov 22 03:49:13 2023
@@ -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.19 2023/11/22 03:49:13 yamaguchi 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 *,

Reply via email to