Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 15 Dec 2015 15:37:24 +0000
From:      "rpokala (Ravi Pokala)" <phabric-noreply@FreeBSD.org>
To:        freebsd-net@freebsd.org
Subject:   [Differential] [Updated, 203 lines] D1986: Teach lagg(4) to change MTU
Message-ID:  <7201bd5d6eef08f9d7dd90d39c21fe10@localhost.localdomain>
In-Reply-To: <differential-rev-PHID-DREV-i34kfg4qpajia7fo5u5l-req@FreeBSD.org>
References:  <differential-rev-PHID-DREV-i34kfg4qpajia7fo5u5l-req@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
rpokala updated this revision to Diff 11321.
rpokala added a comment.


  Updated patch from LN, addressing comments from hrs@, melifaro@, and internal Panasas review.

REPOSITORY
  rS FreeBSD src repository

CHANGES SINCE LAST UPDATE
  https://reviews.freebsd.org/D1986?vs=4079&id=11321

BRANCH
  /head

REVISION DETAIL
  https://reviews.freebsd.org/D1986

AFFECTED FILES
  sys/net/if_lagg.c
  sys/net/if_lagg.h

EMAIL PREFERENCES
  https://reviews.freebsd.org/settings/panel/emailpreferences/

To: rpokala, rstone, rpokala-panasas.com
Cc: imp, melifaro, hrs, sbruno, lakshmi.n_msystechnologies.com, emaste, ae, freebsd-net-list

[-- Attachment #2 --]
diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h
--- a/sys/net/if_lagg.h
+++ b/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;
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
--- a/sys/net/if_lagg.c
+++ b/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,35 +678,197 @@
 
 	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);
 	head = SLIST_FIRST(&sc->sc_llq_head);
 	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);


Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?7201bd5d6eef08f9d7dd90d39c21fe10>