mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-14 10:09:48 +00:00
Call if_setlladdr() on the aggregation port from a taskqueue so the softc lock
is not held. The short delay between aggregating the port and setting the MAC address is fine.
This commit is contained in:
parent
108fe96a44
commit
cdc6f95f84
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=169329
@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/hash.h>
|
||||
#include <sys/taskqueue.h>
|
||||
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
@ -81,6 +82,7 @@ static void lagg_clone_destroy(struct ifnet *);
|
||||
static void lagg_lladdr(struct lagg_softc *, uint8_t *);
|
||||
static int lagg_capabilities(struct lagg_softc *);
|
||||
static void lagg_port_lladdr(struct lagg_port *, uint8_t *);
|
||||
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);
|
||||
static struct mbuf *lagg_input(struct ifnet *, struct mbuf *);
|
||||
@ -221,6 +223,7 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
}
|
||||
LAGG_LOCK_INIT(sc);
|
||||
SLIST_INIT(&sc->sc_ports);
|
||||
TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc);
|
||||
|
||||
/* Initialise pseudo media types */
|
||||
ifmedia_init(&sc->sc_media, 0, lagg_media_change,
|
||||
@ -285,6 +288,7 @@ lagg_clone_destroy(struct ifnet *ifp)
|
||||
SLIST_REMOVE(&lagg_list, sc, lagg_softc, sc_entries);
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
|
||||
taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task);
|
||||
LAGG_LOCK_DESTROY(sc);
|
||||
free(sc, M_DEVBUF);
|
||||
}
|
||||
@ -329,18 +333,74 @@ lagg_capabilities(struct lagg_softc *sc)
|
||||
static void
|
||||
lagg_port_lladdr(struct lagg_port *lp, uint8_t *lladdr)
|
||||
{
|
||||
struct lagg_softc *sc = lp->lp_lagg;
|
||||
struct ifnet *ifp = lp->lp_ifp;
|
||||
int error;
|
||||
struct lagg_llq *llq;
|
||||
int pending = 0;
|
||||
|
||||
LAGG_LOCK_ASSERT(sc);
|
||||
|
||||
if (lp->lp_detaching ||
|
||||
memcmp(lladdr, IF_LLADDR(ifp), ETHER_ADDR_LEN) == 0)
|
||||
return;
|
||||
|
||||
/* Set the link layer address */
|
||||
error = if_setlladdr(ifp, lladdr, ETHER_ADDR_LEN);
|
||||
if (error)
|
||||
printf("%s: setlladdr failed on %s\n", __func__, lp->lp_ifname);
|
||||
/* 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) {
|
||||
pending = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT);
|
||||
if (llq == NULL) /* XXX what to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the lladdr even if pending, it may have changed */
|
||||
llq->llq_ifp = ifp;
|
||||
bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
|
||||
|
||||
if (!pending)
|
||||
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.
|
||||
*/
|
||||
static void
|
||||
lagg_port_setlladdr(void *arg, int pending)
|
||||
{
|
||||
struct lagg_softc *sc = (struct lagg_softc *)arg;
|
||||
struct lagg_llq *llq, *head;
|
||||
struct ifnet *ifp;
|
||||
int error;
|
||||
|
||||
/* Grab a local reference of the queue and remove it from the softc */
|
||||
LAGG_LOCK(sc);
|
||||
head = SLIST_FIRST(&sc->sc_llq_head);
|
||||
SLIST_FIRST(&sc->sc_llq_head) = NULL;
|
||||
LAGG_UNLOCK(sc);
|
||||
|
||||
/*
|
||||
* Traverse the queue and set the lladdr on each ifp. It is safe to do
|
||||
* unlocked as we have the only reference to it.
|
||||
*/
|
||||
for (llq = head; llq != NULL; llq = head) {
|
||||
ifp = llq->llq_ifp;
|
||||
|
||||
/* Set the link layer address */
|
||||
error = if_setlladdr(ifp, llq->llq_lladdr, ETHER_ADDR_LEN);
|
||||
if (error)
|
||||
printf("%s: setlladdr failed on %s\n", __func__,
|
||||
ifp->if_xname);
|
||||
|
||||
head = SLIST_NEXT(llq, llq_entries);
|
||||
free(llq, M_DEVBUF);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -461,6 +521,7 @@ lagg_port_destroy(struct lagg_port *lp, int runpd)
|
||||
{
|
||||
struct lagg_softc *sc = lp->lp_lagg;
|
||||
struct lagg_port *lp_ptr;
|
||||
struct lagg_llq *llq;
|
||||
struct ifnet *ifp = lp->lp_ifp;
|
||||
|
||||
LAGG_LOCK_ASSERT(sc);
|
||||
@ -506,6 +567,18 @@ lagg_port_destroy(struct lagg_port *lp, int runpd)
|
||||
lagg_port_lladdr(lp_ptr, lladdr);
|
||||
}
|
||||
|
||||
/* Remove any pending lladdr changes from the queue */
|
||||
if (lp->lp_detaching) {
|
||||
SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) {
|
||||
if (llq->llq_ifp == ifp) {
|
||||
SLIST_REMOVE(&sc->sc_llq_head, llq, lagg_llq,
|
||||
llq_entries);
|
||||
free(llq, M_DEVBUF);
|
||||
break; /* Only appears once */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->lp_ifflags)
|
||||
if_printf(ifp, "%s: lp_ifflags unclean\n", __func__);
|
||||
|
||||
|
@ -140,6 +140,13 @@ struct lagg_mc {
|
||||
SLIST_ENTRY(lagg_mc) mc_entries;
|
||||
};
|
||||
|
||||
/* List of interfaces to have the MAC address modified */
|
||||
struct lagg_llq {
|
||||
struct ifnet *llq_ifp;
|
||||
uint8_t llq_lladdr[ETHER_ADDR_LEN];
|
||||
SLIST_ENTRY(lagg_llq) llq_entries;
|
||||
};
|
||||
|
||||
struct lagg_softc {
|
||||
struct ifnet *sc_ifp; /* virtual interface */
|
||||
struct mtx sc_mtx;
|
||||
@ -152,6 +159,10 @@ struct lagg_softc {
|
||||
SLIST_HEAD(__tplhd, lagg_port) sc_ports; /* list of interfaces */
|
||||
SLIST_ENTRY(lagg_softc) sc_entries;
|
||||
|
||||
struct task sc_lladdr_task;
|
||||
SLIST_HEAD(__llqhd, lagg_llq) sc_llq_head; /* interfaces to program
|
||||
the lladdr on */
|
||||
|
||||
/* lagg protocol callbacks */
|
||||
int (*sc_detach)(struct lagg_softc *);
|
||||
int (*sc_start)(struct lagg_softc *, struct mbuf *);
|
||||
|
Loading…
Reference in New Issue
Block a user