svn commit: r290239 - head/sys/net

Alexander V. Chernikov melifaro at FreeBSD.org
Sun Nov 1 19:59:06 UTC 2015


Author: melifaro
Date: Sun Nov  1 19:59:04 2015
New Revision: 290239
URL: https://svnweb.freebsd.org/changeset/base/290239

Log:
  Fix lladdr change propagation for on vlans on top of it.
  Fix lladdr update when setting mac address manually.
  Fix lladdr_event for slave ports addition.
  
  MFC after:		4 weeks
  Sponsored by:		Yandex LLC
  Differential Revision:	https://reviews.freebsd.org/D4004

Modified:
  head/sys/net/if.c
  head/sys/net/if_lagg.c
  head/sys/net/if_lagg.h

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c	Sun Nov  1 19:55:32 2015	(r290238)
+++ head/sys/net/if.c	Sun Nov  1 19:59:04 2015	(r290239)
@@ -3314,8 +3314,10 @@ if_delmulti_locked(struct ifnet *ifp, st
  *
  * At this time we only support certain types of interfaces,
  * and we don't allow the length of the address to change.
+ *
+ * Set noinline to be dtrace-friendly
  */
-int
+__noinline int
 if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len)
 {
 	struct sockaddr_dl *sdl;

Modified: head/sys/net/if_lagg.c
==============================================================================
--- head/sys/net/if_lagg.c	Sun Nov  1 19:55:32 2015	(r290238)
+++ head/sys/net/if_lagg.c	Sun Nov  1 19:59:04 2015	(r290239)
@@ -100,7 +100,7 @@ static const char laggname[] = "lagg";
 
 static void	lagg_lladdr(struct lagg_softc *, uint8_t *);
 static void	lagg_capabilities(struct lagg_softc *);
-static void	lagg_port_lladdr(struct lagg_port *, uint8_t *);
+static void	lagg_port_lladdr(struct lagg_port *, uint8_t *, lagg_llqtype);
 static void	lagg_port_setlladdr(void *, int);
 static int	lagg_port_create(struct lagg_softc *, struct ifnet *);
 static int	lagg_port_destroy(struct lagg_port *, int);
@@ -543,6 +543,7 @@ lagg_clone_destroy(struct ifnet *ifp)
 		lagg_port_destroy(lp, 1);
 	/* Unhook the aggregation protocol */
 	lagg_proto_detach(sc);
+	LAGG_UNLOCK_ASSERT(sc);
 
 	ifmedia_removeall(&sc->sc_media);
 	ether_ifdetach(ifp);
@@ -557,7 +558,12 @@ lagg_clone_destroy(struct ifnet *ifp)
 	free(sc, M_DEVBUF);
 }
 
-static void
+/*
+ * Set link-layer address on the lagg interface itself.
+ * 
+ * Set noinline to be dtrace-friendly
+ */
+static __noinline void
 lagg_lladdr(struct lagg_softc *sc, uint8_t *lladdr)
 {
 	struct ifnet *ifp = sc->sc_ifp;
@@ -577,11 +583,16 @@ lagg_lladdr(struct lagg_softc *sc, uint8
 	bcopy(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN);
 	lagg_proto_lladdr(sc);
 
+	/*
+	 * Send notification request for lagg interface
+	 * itself. Note that new lladdr is already set.
+	 */
 	bzero(&lp, sizeof(lp));
 	lp.lp_ifp = sc->sc_ifp;
 	lp.lp_softc = sc;
 
-	lagg_port_lladdr(&lp, lladdr);
+	/* Do not request lladdr change */
+	lagg_port_lladdr(&lp, lladdr, LAGG_LLQTYPE_VIRT);
 }
 
 static void
@@ -622,51 +633,58 @@ lagg_capabilities(struct lagg_softc *sc)
 	}
 }
 
-static void
-lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr)
+/*
+ * Enqueue interface lladdr notification.
+ * If request is already queued, it is updated.
+ * If setting lladdr is also desired, @do_change has to be set to 1.
+ *
+ * Set noinline to be dtrace-friendly
+ */
+static __noinline void
+lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr, lagg_llqtype llq_type)
 {
 	struct lagg_softc *sc = lp->lp_softc;
 	struct ifnet *ifp = lp->lp_ifp;
 	struct lagg_llq *llq;
-	int pending = 0;
-	int primary;
 
 	LAGG_WLOCK_ASSERT(sc);
 
-	primary = (sc->sc_primary->lp_ifp == ifp) ? 1 : 0;
-	if (primary == 0 && (lp->lp_detaching ||
-	    memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0))
+	/*
+	 * Do not enqueue requests where lladdr is the same for
+	 * "physical" interfaces (e.g. ports in lagg)
+	 */
+	if (llq_type == LAGG_LLQTYPE_PHYS &&
+	    memcmp(IF_LLADDR(ifp), lladdr, ETHER_ADDR_LEN) == 0)
 		return;
 
 	/* Check to make sure its not already queued to be changed */
 	SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) {
-		if (llq->llq_ifp == ifp && llq->llq_primary == primary) {
-			pending = 1;
-			break;
+		if (llq->llq_ifp == ifp) {
+			/* Update lladdr, it may have changed */
+			bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
+			return;
 		}
 	}
 
-	if (!pending) {
-		llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT);
-		if (llq == NULL)	/* XXX what to do */
-			return;
-	}
+	llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (llq == NULL)	/* XXX what to do */
+		return;
 
-	/* Update the lladdr even if pending, it may have changed */
 	llq->llq_ifp = ifp;
-	llq->llq_primary = primary;
+	llq->llq_type = llq_type;
 	bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
-
-	if (!pending)
-		SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
+	/* XXX: We should insert to tail */
+	SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
 
 	taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task);
 }
 
 /*
  * Set the interface MAC address from a taskqueue to avoid a LOR.
+ *
+ * Set noinline to be dtrace-friendly
  */
-static void
+static __noinline void
 lagg_port_setlladdr(void *arg, int pending)
 {
 	struct lagg_softc *sc = (struct lagg_softc *)arg;
@@ -688,17 +706,20 @@ lagg_port_setlladdr(void *arg, int pendi
 		ifp = llq->llq_ifp;
 
 		CURVNET_SET(ifp->if_vnet);
-		if (llq->llq_primary == 0) {
-			/*
-			 * Set the link layer address on the laggport interface.
-			 * if_setlladdr() triggers gratuitous ARPs for INET.
-			 */
+		error = 0;
+
+		/*
+		 * Set the link layer address on the laggport interface.
+		 * Note that if_setlladdr() or iflladdr_event handler
+		 * may result in arp transmission / lltable updates.
+		 */
+		if (llq->llq_type == LAGG_LLQTYPE_PHYS)
 			error = if_setlladdr(ifp, llq->llq_lladdr,
 			    ETHER_ADDR_LEN);
-			if (error)
-				printf("%s: setlladdr failed on %s\n", __func__,
-				    ifp->if_xname);
-		} else
+		if (error)
+			printf("%s: setlladdr failed on %s\n", __func__,
+			    ifp->if_xname);
+		else
 			EVENTHANDLER_INVOKE(iflladdr_event, ifp);
 		CURVNET_RESTORE();
 		head = SLIST_NEXT(llq, llq_entries);
@@ -730,7 +751,7 @@ lagg_port_create(struct lagg_softc *sc, 
 	}
 
 	/* XXX Disallow non-ethernet interfaces (this should be any of 802) */
-	if (ifp->if_type != IFT_ETHER)
+	if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN)
 		return (EPROTONOSUPPORT);
 
 	/* Allow the first Ethernet member to define the MTU */
@@ -784,10 +805,15 @@ lagg_port_create(struct lagg_softc *sc, 
 
 	if (SLIST_EMPTY(&sc->sc_ports)) {
 		sc->sc_primary = lp;
+		/* First port in lagg. Update/notify lagg lladdress */
 		lagg_lladdr(sc, IF_LLADDR(ifp));
 	} else {
-		/* Update link layer address for this port */
-		lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp));
+
+		/*
+		 * Update link layer address for this port and
+		 * send notifications to other subsystems.
+		 */
+		lagg_port_lladdr(lp, IF_LLADDR(sc->sc_ifp), LAGG_LLQTYPE_PHYS);
 	}
 
 	/*
@@ -873,7 +899,7 @@ lagg_port_destroy(struct lagg_port *lp, 
 	if (!lp->lp_detaching) {
 		lagg_ether_cmdmulti(lp, 0);
 		lagg_setflags(lp, 0);
-		lagg_port_lladdr(lp, lp->lp_lladdr);
+		lagg_port_lladdr(lp, lp->lp_lladdr, LAGG_LLQTYPE_PHYS);
 	}
 
 	/* Restore interface */
@@ -905,19 +931,16 @@ lagg_port_destroy(struct lagg_port *lp, 
 		}
 		lagg_lladdr(sc, lladdr);
 
+		/* Mark lp0 as new primary */
+		sc->sc_primary = lp0;
+
 		/*
-		 * Update link layer address for each port.  No port is
-		 * marked as primary at this moment.
+		 * Enqueue lladdr update/notification for each port
+		 * (new primary needs update as well, to switch from
+		 * old lladdr to its 'real' one).
 		 */
 		SLIST_FOREACH(lp_ptr, &sc->sc_ports, lp_entries)
-			lagg_port_lladdr(lp_ptr, lladdr);
-		/*
-		 * Mark lp0 as the new primary.  This invokes an
-		 * iflladdr_event.
-		 */
-		sc->sc_primary = lp0;
-		if (lp0 != NULL)
-			lagg_port_lladdr(lp0, lladdr);
+			lagg_port_lladdr(lp_ptr, lladdr, LAGG_LLQTYPE_PHYS);
 	}
 
 	/* Remove any pending lladdr changes from the queue */
@@ -1149,8 +1172,8 @@ static void
 lagg_init(void *xsc)
 {
 	struct lagg_softc *sc = (struct lagg_softc *)xsc;
-	struct lagg_port *lp;
 	struct ifnet *ifp = sc->sc_ifp;
+	struct lagg_port *lp;
 
 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
 		return;
@@ -1158,9 +1181,14 @@ lagg_init(void *xsc)
 	LAGG_WLOCK(sc);
 
 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
-	/* Update the port lladdrs */
+
+	/*
+	 * Update the port lladdrs if needed.
+	 * This might be if_setlladdr() notification
+	 * that lladdr has been changed.
+	 */
 	SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
-		lagg_port_lladdr(lp, IF_LLADDR(ifp));
+		lagg_port_lladdr(lp, IF_LLADDR(ifp), LAGG_LLQTYPE_PHYS);
 
 	lagg_proto_init(sc);
 
@@ -1244,6 +1272,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd
 
 		LAGG_WLOCK(sc);
 		lagg_proto_detach(sc);
+		LAGG_UNLOCK_ASSERT(sc);
 		lagg_proto_attach(sc, ra->ra_proto);
 		break;
 	case SIOCGLAGGOPTS:

Modified: head/sys/net/if_lagg.h
==============================================================================
--- head/sys/net/if_lagg.h	Sun Nov  1 19:55:32 2015	(r290238)
+++ head/sys/net/if_lagg.h	Sun Nov  1 19:59:04 2015	(r290239)
@@ -201,11 +201,16 @@ struct lagg_mc {
 	SLIST_ENTRY(lagg_mc)	mc_entries;
 };
 
+typedef enum {
+	LAGG_LLQTYPE_PHYS = 0,	/* Task related to physical (underlying) port */
+	LAGG_LLQTYPE_VIRT,	/* Task related to lagg interface itself */
+} lagg_llqtype;
+
 /* List of interfaces to have the MAC address modified */
 struct lagg_llq {
 	struct ifnet		*llq_ifp;
 	uint8_t			llq_lladdr[ETHER_ADDR_LEN];
-	uint8_t			llq_primary;
+	lagg_llqtype		llq_type;
 	SLIST_ENTRY(lagg_llq)	llq_entries;
 };
 
@@ -273,6 +278,7 @@ struct lagg_port {
 #define	LAGG_WUNLOCK(_sc)	rm_wunlock(&(_sc)->sc_mtx)
 #define	LAGG_RLOCK_ASSERT(_sc)	rm_assert(&(_sc)->sc_mtx, RA_RLOCKED)
 #define	LAGG_WLOCK_ASSERT(_sc)	rm_assert(&(_sc)->sc_mtx, RA_WLOCKED)
+#define	LAGG_UNLOCK_ASSERT(_sc)	rm_assert(&(_sc)->sc_mtx, RA_UNLOCKED)
 
 extern struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *);
 extern void	(*lagg_linkstate_p)(struct ifnet *, int );


More information about the svn-src-head mailing list