Index: sys/net/if_lagg.c =================================================================== --- sys/net/if_lagg.c +++ sys/net/if_lagg.c @@ -132,6 +132,7 @@ static struct lagg_port *lagg_link_active(struct lagg_softc *, struct lagg_port *); static const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *); +static int lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr); /* Simple round robin */ static void lagg_rr_attach(struct lagg_softc *); @@ -1474,10 +1475,12 @@ break; case SIOCSIFCAP: - case SIOCSIFMTU: - /* Do not allow the MTU or caps to be directly changed */ + /* Do not allow the CAPs to be directly changed. */ error = EINVAL; break; + case SIOCSIFMTU: + error = lagg_change_mtu(ifp, ifr); + break; default: error = ether_ioctl(ifp, cmd, data); @@ -1755,6 +1758,73 @@ LAGG_WUNLOCK(sc); } +static int +lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr) +{ + struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; + struct lagg_port *lp; + int old_mtu = ifp->if_mtu; + int new_mtu = ifr->ifr_mtu; + int err = 0, err2 = 0; + struct ifreq ifr_copy; + + if (old_mtu == new_mtu) + return (0); + + bcopy(ifr, &ifr_copy, sizeof(ifr_copy)); + + /* Try to change the MTU on all component interfaces; if that doesn't + * work on any of them, roll back everything to the original value. + */ + LAGG_WLOCK(sc); + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { + bcopy(lp->lp_ifp->if_xname, ifr_copy.ifr_name, + IFNAMSIZ); + if (lp->lp_ioctl == NULL) + err = EOPNOTSUPP; + else + err = lp->lp_ioctl(lp->lp_ifp, SIOCSIFMTU, + (caddr_t) &ifr_copy); + if (err) { + if_printf(lp->lp_ifp, + "Failed to change MTU from %d to %d (err %d)\n", + old_mtu, new_mtu, err); + /* Abort and roll-back after the first error. */ + break; + } + } + + /* No errors => all component interfaces' MTUs were changed. Update + * the MTU of the LAGG as a whole. + */ + if (!err) { + ifp->if_mtu = new_mtu; + LAGG_WUNLOCK(sc); + return (0); + } + + /* If we got here, then one of the component interfaces could not be + * changed. Restore all component interfaces' MTUs to the original + * value. + */ + ifr_copy.ifr_mtu = old_mtu; + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { + bcopy(lp->lp_ifp->if_xname, ifr_copy.ifr_name, + IFNAMSIZ); + if (lp->lp_ioctl == NULL) + err2 = EOPNOTSUPP; + else + err2 = lp->lp_ioctl(lp->lp_ifp, SIOCSIFMTU, + (caddr_t) &ifr_copy); + if (err2) + if_printf(lp->lp_ifp, + "Failed to restore MTU to %d (err %d)\n", + old_mtu, err2); + } + LAGG_WUNLOCK(sc); + return (err); +} + struct lagg_port * lagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) { Index: sys/net/if_lagg.c =================================================================== --- sys/net/if_lagg.c +++ sys/net/if_lagg.c @@ -132,6 +132,7 @@ static struct lagg_port *lagg_link_active(struct lagg_softc *, struct lagg_port *); static const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *); +static int lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr); /* Simple round robin */ static void lagg_rr_attach(struct lagg_softc *); @@ -1474,10 +1475,12 @@ break; case SIOCSIFCAP: - case SIOCSIFMTU: - /* Do not allow the MTU or caps to be directly changed */ + /* Do not allow the CAPs to be directly changed. */ error = EINVAL; break; + case SIOCSIFMTU: + error = lagg_change_mtu(ifp, ifr); + break; default: error = ether_ioctl(ifp, cmd, data); @@ -1755,6 +1758,72 @@ LAGG_WUNLOCK(sc); } +static int +lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr) +{ + struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc; + struct lagg_port *lp; + int old_mtu = ifp->if_mtu; + int new_mtu = ifr->ifr_mtu; + int err = 0, err2 = 0; + struct ifreq ifr_copy; + struct rm_priotracker tracker; + + if (old_mtu == new_mtu) { + return 0; + } + + bcopy(ifr, &ifr_copy, sizeof(ifr_copy)); + + /* Try to change the MTU on all component interfaces; if that doesn't + * work on any of them, for any reason, back everything out. + */ + LAGG_RLOCK(sc, &tracker); + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { + if (lp->lp_ioctl == NULL) { + err = EOPNOTSUPP; + } else { + bcopy(lp->lp_ifp->if_xname, ifr_copy.ifr_name, + IFNAMSIZ); + err = lp->lp_ioctl(lp->lp_ifp, SIOCSIFMTU, + (caddr_t)&ifr_copy); + } + if (err) { + if_printf(lp->lp_ifp, + "Failure changing MTU to %d (err %d)\n", + new_mtu, err); + break; + } + } + + if (!err) { + /* We are good */ + ifp->if_mtu = new_mtu; + LAGG_RUNLOCK(sc, &tracker); + return 0; + } + + /* restore mtu */ + ifr_copy.ifr_mtu = old_mtu; + SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) { + if (lp->lp_ioctl == NULL) { + continue; + } else { + bcopy(lp->lp_ifp->if_xname, ifr_copy.ifr_name, + IFNAMSIZ); + err2 = lp->lp_ioctl(lp->lp_ifp, SIOCSIFMTU, + (caddr_t)&ifr_copy); + } + if (err2) { + if_printf(lp->lp_ifp, + "Failure restoring MTU to %d (err %d)\n", + old_mtu, err2); + } + } + LAGG_RUNLOCK(sc, &tracker); + return err; +} + struct lagg_port * lagg_link_active(struct lagg_softc *sc, struct lagg_port *lp) {