Page MenuHomeFreeBSD

D1986.id11321.diff
No OneTemporary

D1986.id11321.diff

Index: sys/net/if_lagg.h
===================================================================
--- sys/net/if_lagg.h
+++ sys/net/if_lagg.h
@@ -208,9 +208,19 @@
/* List of interfaces to have the MAC address modified */
struct lagg_llq {
+
struct ifnet *llq_ifp;
uint8_t llq_lladdr[ETHER_ADDR_LEN];
lagg_llqtype llq_type;
+
+#define LAGG_ASYNC_SET_MAC_ADDR 0x0001
+#define LAGG_ASYNC_SET_MTU 0x0002
+#define LAGG_ASYNC_SET_MASK (LAGG_ASYNC_SET_MAC_ADDR | LAGG_ASYNC_SET_MTU)
+
+ uint16_t flags;
+ struct ifreq *ifr; /* Common to all lagg member ports */
+ uint32_t old_mtu;
+ int (*llq_ioctl)(struct ifnet *, u_long, caddr_t);
SLIST_ENTRY(lagg_llq) llq_entries;
};
@@ -235,9 +245,9 @@
SLIST_HEAD(__tplhd, lagg_port) sc_ports; /* list of interfaces */
SLIST_ENTRY(lagg_softc) sc_entries;
- struct task sc_lladdr_task;
+ struct task sc_llq_task; /* ASYNC ops enqueued here */
SLIST_HEAD(__llqhd, lagg_llq) sc_llq_head; /* interfaces to program
- the lladdr on */
+ the MAC, lladdr on */
eventhandler_tag vlan_attach;
eventhandler_tag vlan_detach;
struct callout sc_callout;
Index: sys/net/if_lagg.c
===================================================================
--- sys/net/if_lagg.c
+++ sys/net/if_lagg.c
@@ -101,7 +101,9 @@
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 *, lagg_llqtype);
-static void lagg_port_setlladdr(void *, int);
+static void lagg_port_ops(void *, int);
+static void lagg_port_set_mtu(struct lagg_softc *, struct lagg_llq *);
+static void lagg_port_setlladdr(struct lagg_llq *);
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 *);
@@ -130,6 +132,10 @@
static void lagg_media_status(struct ifnet *, struct ifmediareq *);
static struct lagg_port *lagg_link_active(struct lagg_softc *,
struct lagg_port *);
+static int lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr);
+static struct lagg_llq *lagg_get_llq_entry(struct lagg_softc *,
+ struct ifnet *);
+static void lagg_llq_list_cleanup(struct lagg_softc *);
/* Simple round robin */
static void lagg_rr_attach(struct lagg_softc *);
@@ -487,7 +493,7 @@
LAGG_LOCK_INIT(sc);
SLIST_INIT(&sc->sc_ports);
- TASK_INIT(&sc->sc_lladdr_task, 0, lagg_port_setlladdr, sc);
+ TASK_INIT(&sc->sc_llq_task, 0, lagg_port_ops, sc);
/* Initialise pseudo media types */
ifmedia_init(&sc->sc_media, 0, lagg_media_change,
@@ -553,7 +559,7 @@
SLIST_REMOVE(&V_lagg_list, sc, lagg_softc, sc_entries);
LAGG_LIST_UNLOCK();
- taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task);
+ taskqueue_drain(taskqueue_swi, &sc->sc_llq_task);
LAGG_LOCK_DESTROY(sc);
free(sc, M_DEVBUF);
}
@@ -672,24 +678,24 @@
llq->llq_ifp = ifp;
llq->llq_type = llq_type;
+ llq->flags |= LAGG_ASYNC_SET_MAC_ADDR;
bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
/* XXX: We should insert to tail */
SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
- taskqueue_enqueue(taskqueue_swi, &sc->sc_lladdr_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_llq_task);
}
/*
- * Set the interface MAC address from a taskqueue to avoid a LOR.
+ * Set the interface MTU, MAC address from a taskqueue to avoid a LOR.
*
* Set noinline to be dtrace-friendly
*/
static __noinline void
-lagg_port_setlladdr(void *arg, int pending)
+lagg_port_ops(void *arg, int pending)
{
struct lagg_softc *sc = (struct lagg_softc *)arg;
struct lagg_llq *llq, *head;
- struct ifnet *ifp;
/* Grab a local reference of the queue and remove it from the softc */
LAGG_WLOCK(sc);
@@ -697,10 +703,172 @@
SLIST_FIRST(&sc->sc_llq_head) = NULL;
LAGG_WUNLOCK(sc);
+ if (!head)
+ return;
+
/*
* Traverse the queue and set the lladdr on each ifp. It is safe to do
* unlocked as we have the only reference to it.
*/
+
+ if (head->flags & LAGG_ASYNC_SET_MTU) {
+ /*
+ * Set the new MTU on the lagg port and its members.
+ * If failed, REVERT to the old MTU.
+ */
+ lagg_port_set_mtu(sc, head);
+ }
+
+ if (head->flags & LAGG_ASYNC_SET_MAC_ADDR) {
+ /*
+ * Set the MAC address.
+ */
+ lagg_port_setlladdr(head);
+ }
+
+ if (!(head->flags & LAGG_ASYNC_SET_MASK))
+ if_printf(sc->sc_ifp,
+ "Lagg port ops: flag (0x%x) not supported\n", head->flags);
+
+ /* Finally free the per port llq entry; DO THIS OPERATION LAST */
+ for (llq = head; llq != NULL; llq = head) {
+ head = SLIST_NEXT(llq, llq_entries);
+ free(llq, M_DEVBUF);
+ }
+}
+
+static void
+lagg_port_set_mtu(struct lagg_softc *sc, struct lagg_llq *head)
+{
+ struct lagg_llq *llq;
+ int err = 0;
+
+ /* Set the new MTU on the lagg interface */
+ LAGG_WLOCK(sc);
+ sc->sc_ifp->if_mtu = head->ifr->ifr_mtu;
+ LAGG_WUNLOCK(sc);
+
+ /* Set the new MTU on the physical interface */
+ for (llq = head; llq != NULL; llq = SLIST_NEXT(llq, llq_entries)) {
+ err = (*llq->llq_ioctl)(llq->llq_ifp, SIOCSIFMTU, (caddr_t)llq->ifr);
+ if (err) {
+ if_printf(llq->llq_ifp,
+ "Failed to change MTU from %d to %d (err %d)\n",
+ llq->old_mtu, llq->ifr->ifr_mtu, err);
+ break;
+ }
+ }
+
+ if (err) {
+ /* Restore the old MTU on the lagg interface */
+ LAGG_WLOCK(sc);
+ sc->sc_ifp->if_mtu = head->old_mtu;
+ LAGG_WUNLOCK(sc);
+
+ /* Restore the old MTU on the physical interface */
+ for (llq = head; llq != NULL; llq = SLIST_NEXT(llq, llq_entries)) {
+ llq->ifr->ifr_mtu = llq->old_mtu;
+ err = (*llq->llq_ioctl)
+ (llq->llq_ifp, SIOCSIFMTU, (caddr_t)llq->ifr);
+ if (err) {
+ if_printf(llq->llq_ifp,
+ "Failed to restore MTU to %d (err %d)\n",
+ llq->old_mtu, err);
+ }
+ }
+ }
+
+ /* Common to all lagg member ports */
+ free(head->ifr, M_DEVBUF);
+}
+
+static void lagg_llq_list_cleanup(struct lagg_softc *sc)
+{
+ struct lagg_llq *head, *llq;
+
+ LAGG_WLOCK_ASSERT(sc);
+
+ head = SLIST_FIRST(&sc->sc_llq_head);
+ SLIST_FIRST(&sc->sc_llq_head) = NULL;
+
+ for (llq = head; llq != NULL; llq = head) {
+ head = SLIST_NEXT(llq, llq_entries);
+ free(llq, M_DEVBUF);
+ }
+}
+
+static struct lagg_llq *
+lagg_get_llq_entry(struct lagg_softc *sc, struct ifnet *ifp)
+{
+ struct lagg_llq *llq;
+
+ LAGG_WLOCK_ASSERT(sc);
+
+ SLIST_FOREACH(llq, &sc->sc_llq_head, llq_entries) {
+ if (llq->llq_ifp == ifp) {
+ break;
+ }
+ }
+ return llq;
+}
+
+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;
+ struct lagg_llq *llq;
+ struct ifreq *ifr_copy, *old_ifr = NULL;
+
+ /* No change in MTU */
+ if (ifp->if_mtu == ifr->ifr_mtu)
+ return (0);
+
+ ifr_copy = malloc(sizeof(struct ifreq), M_DEVBUF, M_NOWAIT);
+ if (ifr_copy == NULL)
+ return (ENOMEM);
+
+ bcopy(ifr, ifr_copy, sizeof(struct ifreq));
+ LAGG_WLOCK(sc);
+ /* All lagg ports (MTU change) shall be queued atomic */
+ SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
+ /* Check to make sure its not already queued */
+ llq = lagg_get_llq_entry(sc, lp->lp_ifp);
+ if (!llq) {
+ llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT);
+ if (llq == NULL) {
+ lagg_llq_list_cleanup(sc);
+ LAGG_WUNLOCK(sc);
+ free(ifr_copy, M_DEVBUF);
+ if (old_ifr)
+ free(old_ifr, M_DEVBUF);
+ return (ENOMEM);
+ }
+ SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
+ } else {
+ old_ifr = llq->ifr;
+ }
+
+ /* Update the llq even if pending, it might have updated */
+ llq->ifr = ifr_copy;
+ llq->old_mtu = ifp->if_mtu;
+ llq->llq_ifp = lp->lp_ifp;
+ llq->llq_ioctl = lp->lp_ioctl;
+ llq->flags |= LAGG_ASYNC_SET_MTU;
+ }
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_llq_task);
+ LAGG_WUNLOCK(sc);
+ if (old_ifr)
+ free(old_ifr, M_DEVBUF);
+ return (0);
+}
+
+static void
+lagg_port_setlladdr(struct lagg_llq *head)
+{
+ struct lagg_llq *llq;
+ struct ifnet *ifp;
+
for (llq = head; llq != NULL; llq = head) {
ifp = llq->llq_ifp;
@@ -718,7 +886,6 @@
EVENTHANDLER_INVOKE(iflladdr_event, ifp);
CURVNET_RESTORE();
head = SLIST_NEXT(llq, llq_entries);
- free(llq, M_DEVBUF);
}
}
@@ -1529,10 +1696,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);

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 29, 9:13 PM (14 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27367786
Default Alt Text
D1986.id11321.diff (8 KB)

Event Timeline