Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F136741243
D1986.vs11321.id11984.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D1986.vs11321.id11984.diff
View Options
Index: sys/kern/kern_condvar.c
===================================================================
--- sys/kern/kern_condvar.c
+++ sys/kern/kern_condvar.c
@@ -455,3 +455,16 @@
if (wakeup_swapper)
kick_proc0();
}
+
+/*
+ * Return true if one or more LWPs are waiting on the specified
+ * condition variable.
+ */
+bool
+cv_has_waiters(struct cv *cvp)
+{
+ if (cvp->cv_waiters > 0)
+ return (TRUE);
+ else
+ return (FALSE);
+}
Index: sys/net/if_lagg.h
===================================================================
--- sys/net/if_lagg.h
+++ sys/net/if_lagg.h
@@ -21,6 +21,8 @@
#ifndef _NET_LAGG_H
#define _NET_LAGG_H
+#include <sys/condvar.h>
+
/*
* Global definitions
*/
@@ -206,18 +208,56 @@
LAGG_LLQTYPE_VIRT, /* Task related to lagg interface itself */
} lagg_llqtype;
-/* List of interfaces to have the MAC address modified */
-struct lagg_llq {
+/* Adding new entry here, SHOULD also have relevant entry in llq_action */
+typedef enum {
+ LAGG_LLQ_MIN = 0,
+ LAGG_LLQ_LLADDR = LAGG_LLQ_MIN, /* MAC Address index */
+ LAGG_LLQ_MTU, /* MTU index */
+
+ LAGG_LLQ_MAX /* This SHOULD be the last entry */
+} lagg_llq_idx;
+
+/* Common list entry definition for each taskq operation */
+struct lagg_llq_slist_entry {
+ SLIST_ENTRY(lagg_llq_slist_entry) llq_entries;
+};
+
+/* Context for lladdr llq operation part of lagg soft context */
+struct lagg_lladdr_llq_ctxt {
+ struct lagg_llq_slist_entry llq_cmn; /* This SHOULD be the first
+ member */
struct ifnet *llq_ifp;
uint8_t llq_lladdr[ETHER_ADDR_LEN];
lagg_llqtype llq_type;
- SLIST_ENTRY(lagg_llq) llq_entries;
+};
+
+/* Context for mtu llq operation part of lagg soft context */
+struct lagg_mtu_llq_ctxt {
+ struct lagg_llq_slist_entry llq_cmn; /* This SHOULD be the first
+ member */
+ struct ifnet *llq_ifp;
+ struct ifreq llq_ifr;
+ uint32_t llq_old_mtu;
+ int (*llq_ioctl)(struct ifnet *, u_long, caddr_t);
};
struct lagg_counters {
uint64_t val[IFCOUNTERS];
};
+/* Conditional variables context for lagg operations */
+struct lagg_signal {
+ struct mtx lock;
+ struct cv cv;
+};
+
+/* Lagg MTU context */
+struct lagg_mtu_ctxt {
+ struct lagg_signal mtu_serial; /* Serialize between the commands */
+ struct lagg_signal mtu_sync; /* Synchronize for command completion */
+ int mtu_cmd_ret;
+};
+
struct lagg_softc {
struct ifnet *sc_ifp; /* virtual interface */
struct rmlock sc_mtx;
@@ -235,9 +275,11 @@
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 */
+ struct task sc_llq_task; /* SYNC & ASYNC ops enqueued here */
+ struct lagg_mtu_ctxt sc_mtu_ctxt; /* MTU programming */
+ /* List of LLQs */
+ SLIST_HEAD(__llqhd, lagg_llq_slist_entry) sc_llq[LAGG_LLQ_MAX];
+
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,11 @@
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_llq_action_mtu(struct lagg_softc *,
+ struct lagg_llq_slist_entry *);
+static void lagg_llq_action_lladdr(struct lagg_softc *,
+ struct lagg_llq_slist_entry *);
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 +134,9 @@
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 *, struct ifreq *);
+static void _lagg_llq_cleanup(struct lagg_llq_slist_entry *);
+static void lagg_llq_cleanup(struct lagg_softc *, lagg_llq_idx);
/* Simple round robin */
static void lagg_rr_attach(struct lagg_softc *);
@@ -165,6 +172,24 @@
struct mbuf *);
static void lagg_lacp_lladdr(struct lagg_softc *);
+/*
+ * This action handler shall be called from taskqueue handler for each
+ * submitted operation
+ */
+typedef void (*lagg_llq_action)(struct lagg_softc *,
+ struct lagg_llq_slist_entry *);
+
+/*
+ * lagg llq action Table: Called at the taskqueue context for each
+ * submitted operations.
+ * Contents SHOULD be in sync with lagg_llq_idx index.
+ * New entries shall be appended.
+ */
+static const lagg_llq_action llq_action[LAGG_LLQ_MAX] = {
+ lagg_llq_action_lladdr, /* Maps to LAGG_LLQ_LLADDR index */
+ lagg_llq_action_mtu, /* Maps to LAGG_LLQ_MTU index */
+};
+
/* lagg protocol table */
static const struct lagg_proto {
lagg_proto pr_num;
@@ -487,7 +512,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,
@@ -505,6 +530,13 @@
ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
ifp->if_capenable = ifp->if_capabilities = IFCAP_HWSTATS;
+ mtx_init(&sc->sc_mtu_ctxt.mtu_sync.lock, ifp->if_xname,
+ "mtu_sync_lock", MTX_DEF);
+ mtx_init(&sc->sc_mtu_ctxt.mtu_serial.lock, ifp->if_xname,
+ "mtu_serial_lock", MTX_DEF);
+ cv_init(&sc->sc_mtu_ctxt.mtu_sync.cv, "mtu_sync_cv");
+ cv_init(&sc->sc_mtu_ctxt.mtu_serial.cv, "mtu_serial_cv");
+
/*
* Attach as an ordinary ethernet device, children will be attached
* as special device IFT_IEEE8023ADLAG.
@@ -553,7 +585,11 @@
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);
+ cv_destroy(&sc->sc_mtu_ctxt.mtu_sync.cv);
+ cv_destroy(&sc->sc_mtu_ctxt.mtu_serial.cv);
+ mtx_destroy(&sc->sc_mtu_ctxt.mtu_sync.lock);
+ mtx_destroy(&sc->sc_mtu_ctxt.mtu_serial.lock);
LAGG_LOCK_DESTROY(sc);
free(sc, M_DEVBUF);
}
@@ -645,7 +681,8 @@
{
struct lagg_softc *sc = lp->lp_softc;
struct ifnet *ifp = lp->lp_ifp;
- struct lagg_llq *llq;
+ struct lagg_llq_slist_entry *cmn_llq;
+ struct lagg_lladdr_llq_ctxt *llq_ctxt;
LAGG_WLOCK_ASSERT(sc);
@@ -658,51 +695,212 @@
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) {
+ SLIST_FOREACH(cmn_llq, &sc->sc_llq[LAGG_LLQ_LLADDR], llq_entries) {
+ llq_ctxt = (struct lagg_lladdr_llq_ctxt *)cmn_llq;
+ if (llq_ctxt->llq_ifp == ifp) {
/* Update lladdr, it may have changed */
- bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
+ bcopy(lladdr, llq_ctxt->llq_lladdr, ETHER_ADDR_LEN);
return;
}
}
- llq = malloc(sizeof(struct lagg_llq), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (llq == NULL) /* XXX what to do */
+ llq_ctxt = malloc(sizeof(struct lagg_lladdr_llq_ctxt), M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (llq_ctxt == NULL) /* XXX what to do */
return;
- llq->llq_ifp = ifp;
- llq->llq_type = llq_type;
- bcopy(lladdr, llq->llq_lladdr, ETHER_ADDR_LEN);
+ llq_ctxt->llq_ifp = ifp;
+ llq_ctxt->llq_type = llq_type;
+ bcopy(lladdr, llq_ctxt->llq_lladdr, ETHER_ADDR_LEN);
/* XXX: We should insert to tail */
- SLIST_INSERT_HEAD(&sc->sc_llq_head, llq, llq_entries);
+ SLIST_INSERT_HEAD(&sc->sc_llq[LAGG_LLQ_LLADDR],
+ (struct lagg_llq_slist_entry *)llq_ctxt, 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;
+ struct lagg_llq_slist_entry *llq_first;
+ lagg_llq_idx llq_idx;
+
+ for (llq_idx = LAGG_LLQ_MIN; llq_idx < LAGG_LLQ_MAX; llq_idx++) {
+ LAGG_WLOCK(sc);
+ llq_first = SLIST_FIRST(&sc->sc_llq[llq_idx]);
+ SLIST_INIT(&sc->sc_llq[llq_idx]);
+ LAGG_WUNLOCK(sc);
+
+ if (llq_first != NULL)
+ llq_action[llq_idx](sc, llq_first);
+ }
+}
- /* Grab a local reference of the queue and remove it from the softc */
+static void
+lagg_llq_action_mtu(struct lagg_softc *sc, struct lagg_llq_slist_entry *first)
+{
+ struct lagg_llq_slist_entry *llq;
+ int err;
+
+ /* Set the new MTU on the lagg interface */
LAGG_WLOCK(sc);
- head = SLIST_FIRST(&sc->sc_llq_head);
- SLIST_FIRST(&sc->sc_llq_head) = NULL;
+ sc->sc_ifp->if_mtu = ((struct lagg_mtu_llq_ctxt *)first)->llq_ifr.ifr_mtu;
LAGG_WUNLOCK(sc);
/*
+ * Traverse the queue and set the mtu on each ifp. It is safe to do
+ * unlocked as we have the only reference to it.
+ */
+ llq = first;
+ SLIST_FOREACH_FROM(llq, (struct __llqhd *)NULL, llq_entries) {
+ struct lagg_mtu_llq_ctxt *llq_ctxt;
+ llq_ctxt = (struct lagg_mtu_llq_ctxt *)llq;
+ /* Set the new MTU on the physical interface */
+ err = (*llq_ctxt->llq_ioctl)(llq_ctxt->llq_ifp, SIOCSIFMTU,
+ (caddr_t)&llq_ctxt->llq_ifr);
+ if (err) {
+ if_printf(llq_ctxt->llq_ifp,
+ "Failed to change MTU from %d to %d (err %d)\n",
+ llq_ctxt->llq_old_mtu, llq_ctxt->llq_ifr.ifr_mtu, err);
+ break;
+ }
+ }
+
+ if (err) {
+ /* Restore the old MTU on the lagg interface */
+ LAGG_WLOCK(sc);
+ sc->sc_ifp->if_mtu = ((struct lagg_mtu_llq_ctxt *)first)->llq_old_mtu;
+ LAGG_WUNLOCK(sc);
+
+ /* Restore the old MTU on the physical interface */
+ llq = first;
+ SLIST_FOREACH_FROM(llq, (struct __llqhd *)NULL, llq_entries) {
+ struct lagg_mtu_llq_ctxt *llq_ctxt;
+ llq_ctxt = (struct lagg_mtu_llq_ctxt *)llq;
+ llq_ctxt->llq_ifr.ifr_mtu = llq_ctxt->llq_old_mtu;
+ err = (*llq_ctxt->llq_ioctl)
+ (llq_ctxt->llq_ifp, SIOCSIFMTU, (caddr_t)&llq_ctxt->llq_ifr);
+ if (err) {
+ if_printf(llq_ctxt->llq_ifp,
+ "Failed to restore MTU to %d (err %d)\n",
+ llq_ctxt->llq_old_mtu, err);
+ }
+ }
+ }
+
+ /* Free the MTU LLQ entries */
+ _lagg_llq_cleanup(first);
+
+ mtx_lock(&sc->sc_mtu_ctxt.mtu_sync.lock);
+ sc->sc_mtu_ctxt.mtu_cmd_ret = err;
+ /* Signal for command completion */
+ cv_signal(&sc->sc_mtu_ctxt.mtu_sync.cv);
+ mtx_unlock(&sc->sc_mtu_ctxt.mtu_sync.lock);
+}
+
+static void _lagg_llq_cleanup(struct lagg_llq_slist_entry *llq)
+{
+ struct lagg_llq_slist_entry *tmp_llq;
+
+ SLIST_FOREACH_FROM_SAFE(llq, (struct __llqhd *)NULL, llq_entries,
+ tmp_llq) {
+ free(llq, M_DEVBUF);
+ }
+}
+
+static void lagg_llq_cleanup(struct lagg_softc *sc, lagg_llq_idx idx)
+{
+ struct lagg_llq_slist_entry *llq;
+
+ LAGG_WLOCK_ASSERT(sc);
+
+ llq = SLIST_FIRST(&sc->sc_llq[idx]);
+ SLIST_INIT(&sc->sc_llq[idx]);
+
+ _lagg_llq_cleanup(llq);
+}
+
+static int
+lagg_change_mtu(struct ifnet *ifp, struct ifreq *ifr)
+{
+ struct lagg_softc *sc;
+ struct lagg_port *lp;
+ struct lagg_mtu_llq_ctxt *llq_ctxt;
+ int ret;
+
+ sc = (struct lagg_softc *)ifp->if_softc;
+
+ mtx_lock(&sc->sc_mtu_ctxt.mtu_serial.lock);
+ if (cv_has_waiters(&sc->sc_mtu_ctxt.mtu_serial.cv) == TRUE) {
+ /* Serialize the command */
+ cv_wait(&sc->sc_mtu_ctxt.mtu_serial.cv,
+ &sc->sc_mtu_ctxt.mtu_serial.lock);
+ }
+ mtx_unlock(&sc->sc_mtu_ctxt.mtu_serial.lock);
+
+ /* No change in MTU */
+ if (ifp->if_mtu == ifr->ifr_mtu) {
+ ret = 0;
+ goto out;
+ }
+
+ LAGG_WLOCK(sc);
+ /* All lagg ports (MTU change) shall be queued atomic */
+ SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
+ llq_ctxt = malloc(sizeof(struct lagg_mtu_llq_ctxt), M_DEVBUF,
+ M_NOWAIT);
+ if (llq_ctxt == NULL) {
+ lagg_llq_cleanup(sc, LAGG_LLQ_MTU);
+ LAGG_WUNLOCK(sc);
+ ret = ENOMEM;
+ goto out;
+ }
+ SLIST_INSERT_HEAD(&sc->sc_llq[LAGG_LLQ_MTU],
+ (struct lagg_llq_slist_entry *)llq_ctxt, llq_entries);
+
+ bcopy(ifr, &llq_ctxt->llq_ifr, sizeof(struct ifreq));
+ llq_ctxt->llq_old_mtu = ifp->if_mtu;
+ llq_ctxt->llq_ifp = lp->lp_ifp;
+ llq_ctxt->llq_ioctl = lp->lp_ioctl;
+ }
+ mtx_lock(&sc->sc_mtu_ctxt.mtu_sync.lock);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_llq_task);
+ LAGG_WUNLOCK(sc);
+
+ /* Wait for the command completion */
+ cv_wait(&sc->sc_mtu_ctxt.mtu_sync.cv, &sc->sc_mtu_ctxt.mtu_sync.lock);
+ ret = sc->sc_mtu_ctxt.mtu_cmd_ret;
+ mtx_unlock(&sc->sc_mtu_ctxt.mtu_sync.lock);
+
+out:
+ mtx_lock(&sc->sc_mtu_ctxt.mtu_serial.lock);
+ /* Signal to process the next command */
+ cv_signal(&sc->sc_mtu_ctxt.mtu_serial.cv);
+ mtx_unlock(&sc->sc_mtu_ctxt.mtu_serial.lock);
+
+ return (ret);
+}
+
+static void
+lagg_llq_action_lladdr(struct lagg_softc *sc, struct lagg_llq_slist_entry *head)
+{
+ struct lagg_lladdr_llq_ctxt *llq_ctxt;
+ struct lagg_llq_slist_entry *llq;
+ struct ifnet *ifp;
+
+ /*
* 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;
+ llq_ctxt = (struct lagg_lladdr_llq_ctxt *)llq;
+ ifp = llq_ctxt->llq_ifp;
CURVNET_SET(ifp->if_vnet);
@@ -711,9 +909,8 @@
* Note that if_setlladdr() or iflladdr_event handler
* may result in arp transmission / lltable updates.
*/
- if (llq->llq_type == LAGG_LLQTYPE_PHYS)
- if_setlladdr(ifp, llq->llq_lladdr,
- ETHER_ADDR_LEN);
+ if (llq_ctxt->llq_type == LAGG_LLQTYPE_PHYS)
+ if_setlladdr(ifp, llq_ctxt->llq_lladdr, ETHER_ADDR_LEN);
else
EVENTHANDLER_INVOKE(iflladdr_event, ifp);
CURVNET_RESTORE();
@@ -877,7 +1074,8 @@
{
struct lagg_softc *sc = lp->lp_softc;
struct lagg_port *lp_ptr, *lp0;
- struct lagg_llq *llq;
+ struct lagg_llq_slist_entry *cmn_llq;
+ struct lagg_lladdr_llq_ctxt *llq_ctxt;
struct ifnet *ifp = lp->lp_ifp;
uint64_t *pval, vdiff;
int i;
@@ -940,11 +1138,12 @@
/* 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);
+ SLIST_FOREACH(cmn_llq, &sc->sc_llq[LAGG_LLQ_LLADDR], llq_entries) {
+ llq_ctxt = (struct lagg_lladdr_llq_ctxt *)cmn_llq;
+ if (llq_ctxt->llq_ifp == ifp) {
+ SLIST_REMOVE(&sc->sc_llq[LAGG_LLQ_LLADDR], cmn_llq,
+ lagg_llq_slist_entry, llq_entries);
+ free(cmn_llq, M_DEVBUF);
break; /* Only appears once */
}
}
@@ -1529,10 +1728,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);
Index: sys/sys/condvar.h
===================================================================
--- sys/sys/condvar.h
+++ sys/sys/condvar.h
@@ -61,6 +61,7 @@
sbintime_t sbt, sbintime_t pr, int flags);
void cv_signal(struct cv *cvp);
+bool cv_has_waiters(struct cv *cvp);
void cv_broadcastpri(struct cv *cvp, int pri);
#define cv_wait(cvp, lock) \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 20, 4:53 AM (6 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25708395
Default Alt Text
D1986.vs11321.id11984.diff (15 KB)
Attached To
Mode
D1986: Teach lagg(4) to change MTU
Attached
Detach File
Event Timeline
Log In to Comment