]> xenbits.xensource.com Git - people/julieng/freebsd.git/commitdiff
Fix lladdr change propagation for on vlans on top of it.
authormelifaro <melifaro@FreeBSD.org>
Sun, 1 Nov 2015 19:59:04 +0000 (19:59 +0000)
committermelifaro <melifaro@FreeBSD.org>
Sun, 1 Nov 2015 19:59:04 +0000 (19:59 +0000)
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

sys/net/if.c
sys/net/if_lagg.c
sys/net/if_lagg.h

index a68d54a7adfffd897ef195ee5a8dbc4645bfdf5c..118e63b0658994dd7a3bae7c3343ce3d9eb8514c 100644 (file)
@@ -3314,8 +3314,10 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
  *
  * 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;
index 095f87066a9cd27e8ddb95e305acbe79a540924a..7eceaa45951224fd1c0585e4a4bd66ddd15c9fb9 100644 (file)
@@ -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_t *lladdr)
        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 pending)
                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, struct ifnet *ifp)
        }
 
        /* 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, struct ifnet *ifp)
 
        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, int rundelport)
        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, int rundelport)
                }
                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, caddr_t data)
 
                LAGG_WLOCK(sc);
                lagg_proto_detach(sc);
+               LAGG_UNLOCK_ASSERT(sc);
                lagg_proto_attach(sc, ra->ra_proto);
                break;
        case SIOCGLAGGOPTS:
index 956c238ece863240a17d2e9ad35cb342a0893dc6..195ac3a677afa91b9af2d6a8f539a8c5ab0a2c9f 100644 (file)
@@ -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 );