aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net/if.c4
-rw-r--r--sys/net/if_lagg.c129
-rw-r--r--sys/net/if_lagg.h8
3 files changed, 89 insertions, 52 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index a68d54a7adff..118e63b06589 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -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;
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 095f87066a9c..7eceaa459512 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -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:
diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h
index 956c238ece86..195ac3a677af 100644
--- a/sys/net/if_lagg.h
+++ b/sys/net/if_lagg.h
@@ -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 );