Page MenuHomeFreeBSD

D36651.diff
No OneTemporary

D36651.diff

diff --git a/sys/net/if.c b/sys/net/if.c
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -476,12 +476,23 @@
}
#ifdef VIMAGE
+static void
+vnet_if_return_one(struct ifnet *ifp)
+{
+ int found __diagused;
+
+ if (ifc_vmove(ifp, ifp->if_home_vnet, NULL) == ENOTSUP) {
+ found = if_unlink_ifnet(ifp, true);
+ MPASS(found);
+ if_vmove(ifp, ifp->if_home_vnet);
+ }
+}
+
static void
vnet_if_return(const void *unused __unused)
{
struct ifnet *ifp, *nifp;
struct ifnet **pending;
- int found __diagused;
int i;
i = 0;
@@ -505,17 +516,16 @@
/* Return all inherited interfaces to their parent vnets. */
CK_STAILQ_FOREACH_SAFE(ifp, &V_ifnet, if_link, nifp) {
if (ifp->if_home_vnet != ifp->if_vnet) {
- found = if_unlink_ifnet(ifp, true);
- MPASS(found);
-
- pending[i++] = ifp;
+ if (if_try_ref(ifp))
+ pending[i++] = ifp;
}
}
IFNET_WUNLOCK();
for (int j = 0; j < i; j++) {
sx_xlock(&ifnet_detach_sxlock);
- if_vmove(pending[j], pending[j]->if_home_vnet);
+ vnet_if_return_one(pending[j]);
+ if_rele(pending[j]);
sx_xunlock(&ifnet_detach_sxlock);
}
@@ -708,7 +718,6 @@
bool
if_try_ref(struct ifnet *ifp)
{
- NET_EPOCH_ASSERT();
return (refcount_acquire_if_not_zero(&ifp->if_refcount));
}
@@ -1350,8 +1359,8 @@
}
sx_xlock(&ifnet_detach_sxlock);
- /* Make sure the VNET is stable. */
- shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet);
+ /* Make sure the VNET and interface are stable. */
+ shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet) || (ifp->if_flags & IFF_DYING);
if (shutdown) {
sx_xunlock(&ifnet_detach_sxlock);
CURVNET_RESTORE();
@@ -1360,6 +1369,14 @@
}
CURVNET_RESTORE();
+ error = ifc_vmove(ifp, pr->pr_vnet, NULL);
+ if (error != ENOTSUP) {
+ sx_xunlock(&ifnet_detach_sxlock);
+ CURVNET_RESTORE();
+ prison_free(pr);
+ return (error);
+ }
+
found = if_unlink_ifnet(ifp, true);
if (! found) {
sx_xunlock(&ifnet_detach_sxlock);
@@ -1424,6 +1441,15 @@
return (EBUSY);
}
+ sx_xlock(&ifnet_detach_sxlock);
+ error = ifc_vmove(ifp, vnet_dst, NULL);
+ sx_xunlock(&ifnet_detach_sxlock);
+ if (error != ENOTSUP) {
+ CURVNET_RESTORE();
+ prison_free(pr);
+ return (error);
+ }
+
/* Get interface back from child jail/vnet. */
found = if_unlink_ifnet(ifp, true);
MPASS(found);
@@ -4262,6 +4288,22 @@
return ((struct ifnet *)ifp)->if_capenable;
}
+char *
+if_copydescr(if_t ifp)
+{
+ char *descrbuf = NULL;
+
+ sx_xlock(&ifdescr_sx);
+ int len = ifp->if_description != NULL ? strlen(ifp->if_description) : 0;
+ if (len > 0) {
+ descrbuf = malloc(len + 1, M_IFDESCR, M_WAITOK | M_ZERO);
+ memcpy(descrbuf, ifp->if_description, len);
+ }
+ sx_xunlock(&ifdescr_sx);
+
+ return (descrbuf);
+}
+
void
if_setdescr(if_t ifp, char *descrbuf)
{
diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h
--- a/sys/net/if_clone.h
+++ b/sys/net/if_clone.h
@@ -55,6 +55,8 @@
typedef int ifc_create_f(struct if_clone *ifc, char *name, size_t maxlen,
struct ifc_data *ifd, struct ifnet **ifpp);
typedef int ifc_destroy_f(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags);
+typedef int ifc_vmove_f(struct if_clone *ifc, struct ifnet *ifp_src,
+ struct ifc_data *ifd, struct ifnet **ifpp);
struct if_clone_addreq {
uint16_t version; /* Always 0 for now */
@@ -64,12 +66,15 @@
ifc_match_f *match_f;
ifc_create_f *create_f;
ifc_destroy_f *destroy_f;
+ ifc_vmove_f *vmove_f;
};
-#define IFC_F_NOGROUP 0x01 /* Creation flag: don't add unit group */
-#define IFC_F_AUTOUNIT 0x02 /* Creation flag: automatically select unit */
-#define IFC_F_SYSSPACE 0x04 /* Cloner callback: params pointer is in kernel memory */
-#define IFC_F_FORCE 0x08 /* Deletion flag: force interface deletion */
+#define IFC_F_NOGROUP 0x01 /* Creation flag: don't add unit group */
+#define IFC_F_AUTOUNIT 0x02 /* Creation flag: automatically select unit */
+#define IFC_F_SYSSPACE 0x04 /* Cloner callback: params pointer is in kernel memory */
+#define IFC_F_FORCE 0x08 /* Deletion flag: force interface deletion */
+#define IFC_F_NOMOVE 0x10 /* Creation flag: moving between vnets unsupported */
+#define IFC_F_MOVED 0x40 /* Deletion flag: indicate interface was moved */
#define IFC_NOGROUP IFC_F_NOGROUP
@@ -82,6 +87,18 @@
bool ifc_unlink_ifp(struct if_clone *ifc, struct ifnet *ifp);
int ifc_copyin(const struct ifc_data *ifd, void *target, size_t len);
+int ifc_vmove(struct ifnet *ifp, struct vnet *vnet, struct ifnet **ifpp);
+
+struct ifc_vparam_data {
+ char ifname[IFNAMSIZ];
+ int unit;
+ struct vnet *home_vnet;
+ char *ifdescr;
+ int mtu;
+};
+void ifc_save_vparams(struct ifnet *ifp, struct ifc_vparam_data *vparams);
+void ifc_apply_vparams(struct ifnet *ifp, struct ifc_vparam_data *vparams);
+
#ifdef CLONE_COMPAT_13
/* Methods. */
diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c
--- a/sys/net/if_clone.c
+++ b/sys/net/if_clone.c
@@ -51,6 +51,12 @@
#include <net/route.h>
#include <net/vnet.h>
+/*
+ * Interface is always linked to the ifc in the home vnet.
+ * Similarly, interface units are referenced in the home vnet
+ *
+ */
+
/* Current IF_MAXUNIT expands maximum to 5 characters. */
#define IFCLOSIZ (IFNAMSIZ - 5)
@@ -75,6 +81,9 @@
ifc_match_f *ifc_match; /* (c) Matcher function */
ifc_create_f *ifc_create; /* (c) Creates new interface */
ifc_destroy_f *ifc_destroy; /* (c) Destroys cloned interface */
+#ifdef VIMAGE
+ ifc_vmove_f *ifc_vmove; /* (c) Moves between vnets */
+#endif
#ifdef CLONE_COMPAT_13
/* (c) Driver specific cloning functions. Called with no locks held. */
@@ -105,6 +114,8 @@
static void if_clone_free(struct if_clone *ifc);
static int if_clone_createif(struct if_clone *ifc, char *name, size_t len,
struct ifc_data *ifd, struct ifnet **ifpp);
+static int if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp,
+ uint32_t flags);
static int ifc_simple_match(struct if_clone *ifc, const char *name);
static int ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit);
@@ -282,6 +293,106 @@
return (ifc);
}
+#ifdef VIMAGE
+/*
+ * "Default" vmove handler deleting and re-creating interface
+ * without any creation params
+ */
+static int
+if_vmove_simple(struct if_clone *ifc, struct ifnet *ifp_src, struct ifc_data *ifd,
+ struct ifnet **ifpp)
+{
+ struct ifc_vparam_data vp = {};
+ int error;
+
+ ifc_save_vparams(ifp_src, &vp);
+
+ CURVNET_SET_QUIET(ifd->vnet);
+ error = (*ifc->ifc_create)(ifc, vp.ifname, sizeof(vp.ifname), ifd, ifpp);
+ ifc_apply_vparams(*ifpp, &vp);
+ CURVNET_RESTORE();
+
+ if (error == 0)
+ error = if_clone_destroyif_flags(ifc, ifp_src, IFC_F_MOVED);
+
+ return (error);
+}
+
+void
+ifc_save_vparams(struct ifnet *ifp, struct ifc_vparam_data *ivd)
+{
+ bzero(ivd, sizeof(*ivd));
+
+ strlcpy(ivd->ifname, if_name(ifp), sizeof(ivd->ifname));
+ ivd->unit = ifp->if_dunit;
+ ivd->home_vnet = ifp->if_home_vnet;
+ ivd->ifdescr = if_copydescr(ifp);
+ ivd->mtu = if_getmtu(ifp);
+}
+
+void
+ifc_apply_vparams(struct ifnet *ifp, struct ifc_vparam_data *vp)
+{
+ if (ifp == NULL) {
+ if (vp->ifdescr != NULL)
+ if_freedescr(vp->ifdescr);
+ return;
+ }
+
+ if (vp->home_vnet != NULL)
+ ifp->if_home_vnet = vp->home_vnet;
+ if (vp->ifdescr != NULL)
+ if_setdescr(ifp, vp->ifdescr);
+ if (vp->mtu > 0)
+ if_setmtu(ifp, vp->mtu);
+}
+
+int
+ifc_vmove(struct ifnet *ifp, struct vnet *vnet,
+ struct ifnet **ifpp)
+{
+ struct ifnet *ifp_dst = NULL;
+ int error = 0;
+
+ sx_assert(&ifnet_detach_sxlock, SA_XLOCKED);
+
+ if (!if_try_ref(ifp))
+ return (EINVAL);
+
+ struct if_clone *ifc = ifc_find_cloner(ifp->if_dname, ifp->if_home_vnet);
+
+ /* Check if the interface with this name exists in target vnet first */
+ CURVNET_SET_QUIET(vnet);
+ bool found = ifunit(if_name(ifp)) != NULL;
+
+ if (found)
+ error = EEXIST;
+ if (ifc == NULL || (ifc->ifc_flags & IFC_F_NOMOVE))
+ error = ENOTSUP;
+
+ if (error == 0) {
+ struct ifc_data ifd = {
+ .vnet = vnet,
+ .flags = IFC_F_MOVED,
+ .unit = ifp->if_dunit,
+ };
+ CURVNET_SET_QUIET(ifp->if_vnet);
+ error = ifc->ifc_vmove(ifc, ifp, &ifd, &ifp_dst);
+ CURVNET_RESTORE();
+ }
+ if_rele(ifp);
+
+ if (error == 0)
+ ifc_link_ifp(ifc, ifp_dst);
+ CURVNET_RESTORE();
+
+ if (ifpp != NULL)
+ *ifpp = ifp_dst;
+
+ return (error);
+}
+#endif
+
/*
* Create a clone network interface.
*/
@@ -290,11 +401,13 @@
struct ifc_data *ifd, struct ifnet **ifpp)
{
int err, unit = 0;
+ bool need_unit;
if (ifunit(name) != NULL)
return (EEXIST);
- if (ifc->ifc_flags & IFC_F_AUTOUNIT) {
+ need_unit = ((ifc->ifc_flags & IFC_F_AUTOUNIT) && (!(ifd->flags & IFC_F_MOVED)));
+ if (need_unit) {
if ((err = ifc_handle_unit(ifc, name, len, &unit)) != 0)
return (err);
ifd->unit = unit;
@@ -305,7 +418,7 @@
if (err == 0) {
MPASS(*ifpp != NULL);
if_clone_addif(ifc, *ifpp);
- } else if (ifc->ifc_flags & IFC_F_AUTOUNIT)
+ } else if (need_unit)
ifc_free_unit(ifc, unit);
return (err);
@@ -344,6 +457,8 @@
{
int err;
+ sx_assert(&ifnet_detach_sxlock, SA_XLOCKED);
+
/*
* Given that the cloned ifnet might be attached to a different
* vnet from where its cloner was registered, we have to
@@ -361,7 +476,7 @@
if (err != 0)
ifc_link_ifp(ifc, ifp);
- else if (ifc->ifc_flags & IFC_F_AUTOUNIT)
+ else if ((ifc->ifc_flags & IFC_F_AUTOUNIT) && (!(flags & IFC_F_MOVED)))
ifc_free_unit(ifc, unit);
CURVNET_RESTORE();
return (err);
@@ -419,10 +534,12 @@
return (NULL);
struct if_clone *ifc = if_clone_alloc(name, req->maxunit);
+
ifc->ifc_match = req->match_f != NULL ? req->match_f : ifc_simple_match;
ifc->ifc_create = req->create_f;
ifc->ifc_destroy = req->destroy_f;
- ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP));
+ ifc->ifc_vmove = (req->vmove_f != NULL) ? req->vmove_f : if_vmove_simple;
+ ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP | IFC_F_NOMOVE));
if (if_clone_attach(ifc) != 0)
return (NULL);
@@ -472,6 +589,7 @@
ifc->ifc_destroy = ifc_advanced_destroy_wrapper;
ifc->ifca_destroy = destroy;
ifc->ifca_create = create;
+ ifc->ifc_flags = IFC_F_NOMOVE;
if (if_clone_attach(ifc) != 0)
return (NULL);
@@ -518,7 +636,7 @@
ifc->ifcs_create = create;
ifc->ifcs_destroy = destroy;
ifc->ifcs_minifs = minifs;
- ifc->ifc_flags = IFC_F_AUTOUNIT;
+ ifc->ifc_flags = IFC_F_AUTOUNIT | IFC_F_NOMOVE;
if (if_clone_attach(ifc) != 0)
return (NULL);
diff --git a/sys/net/if_epair.c b/sys/net/if_epair.c
--- a/sys/net/if_epair.c
+++ b/sys/net/if_epair.c
@@ -118,7 +118,9 @@
struct ifnet *ifp; /* This ifp. */
struct ifnet *oifp; /* other ifp of pair. */
int num_queues;
+ bool is_moved; /* true if moved */
struct epair_queue *queues;
+ struct if_clone *ifc; /* cloner used to create this instance */
struct ifmedia media; /* Media config (fake). */
STAILQ_ENTRY(epair_softc) entry;
};
@@ -232,49 +234,40 @@
}
static void
-epair_menq(struct mbuf *m, struct epair_softc *osc)
+epair_menq(struct epair_queue *q, struct mbuf *m, struct ifnet *input_ifp,
+ struct ifnet *output_ifp)
{
- struct ifnet *ifp, *oifp;
int len, ret;
int ridx;
short mflags;
- /*
- * I know this looks weird. We pass the "other sc" as we need that one
- * and can get both ifps from it as well.
- */
- oifp = osc->ifp;
- ifp = osc->oifp;
-
- epair_prepare_mbuf(m, oifp);
+ epair_prepare_mbuf(m, input_ifp);
/* Save values as once the mbuf is queued, it's not ours anymore. */
len = m->m_pkthdr.len;
mflags = m->m_flags;
- struct epair_queue *q = epair_select_queue(osc, m);
-
atomic_set_long(&q->state, (1 << BIT_MBUF_QUEUED));
ridx = atomic_load_int(&q->ridx);
ret = buf_ring_enqueue(q->rxring[ridx], m);
if (ret != 0) {
/* Ring is full. */
- if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
+ if_inc_counter(output_ifp, IFCOUNTER_OQDROPS, 1);
m_freem(m);
return;
}
- if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(output_ifp, IFCOUNTER_OPACKETS, 1);
/*
* IFQ_HANDOFF_ADJ/ip_handoff() update statistics,
* but as we bypass all this we have to duplicate
* the logic another time.
*/
- if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
+ if_inc_counter(output_ifp, IFCOUNTER_OBYTES, len);
if (mflags & (M_BCAST|M_MCAST))
- if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+ if_inc_counter(output_ifp, IFCOUNTER_OMCASTS, 1);
/* Someone else received the packet. */
- if_inc_counter(oifp, IFCOUNTER_IPACKETS, 1);
+ if_inc_counter(input_ifp, IFCOUNTER_IPACKETS, 1);
if (!atomic_testandset_long(&q->state, BIT_QUEUE_TASK))
taskqueue_enqueue(epair_tasks.tq[q->id], &q->tx_task);
@@ -294,8 +287,7 @@
* other interface (oifp) of our pair.
*/
sc = ifp->if_softc;
- oifp = sc->oifp;
- sc = oifp->if_softc;
+ oifp = atomic_load_ptr(&sc->oifp);
for (;;) {
IFQ_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
@@ -306,13 +298,14 @@
/* In case either interface is not usable drop the packet. */
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
- (oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ (oifp == NULL || oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
(oifp->if_flags & IFF_UP) == 0) {
m_freem(m);
continue;
}
- epair_menq(m, sc);
+ struct epair_queue *q = epair_select_queue(oifp->if_softc, m);
+ epair_menq(q, m, oifp, ifp);
}
}
@@ -354,8 +347,8 @@
* drop the packet.
*/
sc = ifp->if_softc;
- oifp = sc->oifp;
- if ((oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ oifp = atomic_load_ptr(&sc->oifp);
+ if (oifp == NULL || (oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
(oifp->if_flags & IFF_UP) == 0) {
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
m_freem(m);
@@ -385,7 +378,8 @@
IF_UNLOCK(&ifp->if_snd);
#endif
- epair_menq(m, oifp->if_softc);
+ struct epair_queue *q = epair_select_queue(oifp->if_softc, m);
+ epair_menq(q, m, oifp, ifp);
return (0);
}
@@ -481,18 +475,16 @@
}
static void
-epair_clone_add(struct if_clone *ifc, struct epair_softc *scb)
+epair_clone_add(struct if_clone *ifc, const struct ifnet *src_ifp, struct ifnet *dst_ifp)
{
- struct ifnet *ifp;
uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
- ifp = scb->ifp;
/* Copy epairNa etheraddr and change the last byte. */
- memcpy(eaddr, scb->oifp->if_hw_addr, ETHER_ADDR_LEN);
+ memcpy(eaddr, src_ifp->if_hw_addr, ETHER_ADDR_LEN);
eaddr[5] = 0x0b;
- ether_ifattach(ifp, eaddr);
+ ether_ifattach(dst_ifp, eaddr);
- if_clone_addif(ifc, ifp);
+ if_clone_addif(ifc, dst_ifp);
}
static struct epair_softc *
@@ -505,6 +497,7 @@
return (NULL);
sc = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO);
+ sc->ifc = ifc;
sc->ifp = ifp;
sc->num_queues = epair_tasks.tasks;
sc->queues = mallocarray(sc->num_queues, sizeof(struct epair_queue),
@@ -690,7 +683,6 @@
struct ifc_data *ifd, struct ifnet **ifpp)
{
struct epair_softc *sca, *scb;
- struct ifnet *ifp;
char *dp;
int error, unit;
uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
@@ -716,11 +708,10 @@
scb->oifp = sca->ifp;
/* Finish initialization of interface <n>a. */
- ifp = sca->ifp;
epair_setup_ifp(sca, name, unit);
epair_generate_mac(sca, eaddr);
- ether_ifattach(ifp, eaddr);
+ ether_ifattach(sca->ifp, eaddr);
/* Swap the name and finish initialization of interface <n>b. */
dp = name + strlen(name) - 1;
@@ -728,13 +719,12 @@
epair_setup_ifp(scb, name, unit);
- ifp = scb->ifp;
/* We need to play some tricks here for the second interface. */
strlcpy(name, epairname, len);
/* Correctly set the name for the cloner list. */
strlcpy(name, scb->ifp->if_xname, len);
- epair_clone_add(ifc, scb);
+ epair_clone_add(ifc, sca->ifp, scb->ifp);
/*
* Restore name to <n>a as the ifp for this will go into the
@@ -776,47 +766,116 @@
struct ifnet *oifp;
struct epair_softc *sca, *scb;
int unit, error;
+ bool is_moved;
- /*
- * In case we called into if_clone_destroyif() ourselves
- * again to remove the second interface, the softc will be
- * NULL. In that case so not do anything but return success.
- */
- if (ifp->if_softc == NULL)
- return (0);
+ sx_assert(&ifnet_detach_sxlock, SA_XLOCKED);
unit = ifp->if_dunit;
sca = ifp->if_softc;
- oifp = sca->oifp;
- scb = oifp->if_softc;
+ is_moved = sca->is_moved;
+ oifp = atomic_load_ptr(&sca->oifp);
/* Frist get the interfaces down and detached. */
epair_set_state(ifp, false);
- epair_set_state(oifp, false);
-
+ if (oifp != NULL)
+ epair_set_state(oifp, false);
ether_ifdetach(ifp);
- ether_ifdetach(oifp);
-
- /* Third free any queued packets and all the resources. */
- CURVNET_SET_QUIET(oifp->if_vnet);
- epair_drain_rings(scb);
- oifp->if_softc = NULL;
- error = if_clone_destroyif(ifc, oifp);
- if (error)
- panic("%s: if_clone_destroyif() for our 2nd iface failed: %d",
- __func__, error);
- epair_free_sc(scb);
- CURVNET_RESTORE();
+
+ if (oifp != NULL) {
+ CURVNET_SET_QUIET(oifp->if_vnet);
+
+ /* Rely on ifnet_detach_sxlock lock held */
+ scb = oifp->if_softc;
+ scb->oifp = NULL;
+ sca->oifp = NULL;
+
+ bool result = ifc_unlink_ifp(scb->ifc, oifp);
+ KASSERT(result == true, ("%s: unable to unlink interface %s",
+ __func__, if_name(oifp)));
+ error = epair_clone_destroy(scb->ifc, oifp, flags);
+ if (error)
+ panic("%s: if_clone_destroyif() for our 2nd iface failed: %d",
+ __func__, error);
+ CURVNET_RESTORE();
+ }
epair_drain_rings(sca);
epair_free_sc(sca);
/* Last free the cloner unit. */
- ifc_free_unit(ifc, unit);
+ if (!is_moved && oifp != NULL)
+ ifc_free_unit(ifc, unit);
return (0);
}
+static int
+epair_clone_vmove(struct if_clone *ifc, struct ifnet *ifp_src,
+ struct ifc_data *ifd, struct ifnet **ifpp)
+{
+ struct ifc_vparam_data vdata = {};
+ struct ifnet *oifp;
+ struct epair_softc *sca_src, *sca_dst, *scb;
+ uint8_t eaddr[ETHER_ADDR_LEN];
+ int error = 0;
+
+ sx_assert(&ifnet_detach_sxlock, SA_XLOCKED);
+
+ sca_src = ifp_src->if_softc;
+ if (sca_src == NULL)
+ return (EINVAL);
+
+ /* Copy necessary data to re-create the interface */
+ ifc_save_vparams(ifp_src, &vdata);
+
+ /* Create dst interface first to perform atomic swap */
+ CURVNET_SET_QUIET(ifd->vnet);
+ sca_dst = epair_alloc_sc(ifc);
+ if (sca_dst == NULL) {
+ ifc_apply_vparams(NULL, &vdata);
+ CURVNET_RESTORE();
+ return (ENOSPC);
+ }
+
+ if_ref(sca_dst->ifp);
+
+ epair_setup_ifp(sca_dst, vdata.ifname, vdata.unit);
+ memcpy(eaddr, ifp_src->if_hw_addr, sizeof(eaddr));
+ ifc_apply_vparams(sca_dst->ifp, &vdata);
+ ether_ifattach(sca_dst->ifp, eaddr);
+
+ /* Perform swap */
+ oifp = sca_src->oifp;
+ if (oifp != NULL) {
+ scb = oifp->if_softc;
+ scb->oifp = sca_dst->ifp;
+ sca_dst->oifp = scb->ifp;
+ /* Unlink old interface from the pair */
+ sca_src->oifp = NULL;
+ } else
+ error = EINVAL;
+
+ if (error == 0) {
+ /*
+ * Migration successful, destroy old interface.
+ * if_clone_destroyif() mananages vnet automatically.
+ */
+ sca_src->is_moved = true;
+ if_clone_destroyif(ifc, ifp_src);
+ epair_set_state(sca_dst->ifp, true);
+ *ifpp = sca_dst->ifp;
+ } else {
+ /* Migration failed, remove state */
+ sca_dst->is_moved = true;
+ if_clone_destroyif(ifc, sca_dst->ifp);
+ }
+ if_rele(sca_dst->ifp);
+
+ CURVNET_RESTORE();
+
+ return (error);
+}
+
static void
vnet_epair_init(const void *unused __unused)
{
@@ -824,6 +883,7 @@
.match_f = epair_clone_match,
.create_f = epair_clone_create,
.destroy_f = epair_clone_destroy,
+ .vmove_f = epair_clone_vmove,
};
V_epair_cloner = ifc_attach_cloner(epairname, &req);
}
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -739,6 +739,7 @@
int if_setcapenablebit(if_t ifp, int setcap, int clearcap);
int if_getcapenable(if_t ifp);
const char *if_getdname(if_t ifp);
+char *if_copydescr(if_t ifp);
void if_setdescr(if_t ifp, char *descrbuf);
void if_freedescr(char *descrbuf);
int if_setdev(if_t ifp, void *dev);
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -326,6 +326,8 @@
static int vlan_clone_create(struct if_clone *, char *, size_t,
struct ifc_data *, struct ifnet **);
static int vlan_clone_destroy(struct if_clone *, struct ifnet *, uint32_t);
+static int vlan_clone_vmove(struct if_clone *, struct ifnet *,
+ struct ifc_data *, struct ifnet **);
static void vlan_ifdetach(void *arg, struct ifnet *ifp);
static void vlan_iflladdr(void *arg, struct ifnet *ifp);
@@ -907,6 +909,7 @@
.match_f = vlan_clone_match,
.create_f = vlan_clone_create,
.destroy_f = vlan_clone_destroy,
+ .vmove_f = vlan_clone_vmove,
};
static int
@@ -1061,6 +1064,17 @@
return (1);
}
+struct vlanparams {
+ struct ifnet *parent;
+ int vid;
+ uint16_t proto;
+ uint8_t pcp;
+};
+
+static int
+vlan_create_specific(struct if_clone *ifc, char *name, int unit,
+ struct vlanparams *vp, struct ifnet **ifpp);
+
static int
vlan_clone_create(struct if_clone *ifc, char *name, size_t len,
struct ifc_data *ifd, struct ifnet **ifpp)
@@ -1072,13 +1086,8 @@
int error;
int vid = 0;
uint16_t proto = ETHERTYPE_VLAN;
- struct ifvlan *ifv;
- struct ifnet *ifp;
struct ifnet *p = NULL;
- struct ifaddr *ifa;
- struct sockaddr_dl *sdl;
struct vlanreq vlr;
- static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
/*
@@ -1159,14 +1168,38 @@
}
}
+ struct vlanparams vp = {
+ .parent = p,
+ .vid = vid,
+ .proto = proto,
+ };
+
+ error = vlan_create_specific(ifc, name, unit, &vp, ifpp);
+ if (error != 0) {
+ if (!subinterface)
+ ifc_free_unit(ifc, unit);
+ }
+ if (p != NULL)
+ if_rele(p);
+
+ return (error);
+}
+
+static int
+vlan_create_specific(struct if_clone *ifc, char *name, int unit,
+ struct vlanparams *vp, struct ifnet **ifpp)
+{
+ struct ifvlan *ifv;
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
+ int error;
+
ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL) {
- if (!subinterface)
- ifc_free_unit(ifc, unit);
free(ifv, M_VLAN);
- if (p != NULL)
- if_rele(p);
return (ENOSPC);
}
CK_SLIST_INIT(&ifv->vlan_mc_listhead);
@@ -1205,9 +1238,8 @@
sdl = (struct sockaddr_dl *)ifa->ifa_addr;
sdl->sdl_type = IFT_L2VLAN;
- if (p != NULL) {
- error = vlan_config(ifv, p, vid, proto);
- if_rele(p);
+ if (vp->parent != NULL) {
+ error = vlan_config(ifv, vp->parent, vp->vid, vp->proto);
if (error != 0) {
/*
* Since we've partially failed, we need to back
@@ -1217,8 +1249,6 @@
ether_ifdetach(ifp);
vlan_unconfig(ifp);
if_free(ifp);
- if (!subinterface)
- ifc_free_unit(ifc, unit);
free(ifv, M_VLAN);
return (error);
@@ -1258,6 +1288,45 @@
return (0);
}
+static int
+vlan_clone_vmove(struct if_clone *ifc, struct ifnet *ifp_src,
+ struct ifc_data *ifd, struct ifnet **ifpp)
+{
+ struct ifc_vparam_data vdata = {};
+ struct ifvlan *ifv;
+ int error;
+
+ VLAN_XLOCK();
+
+ /* Copy necessary data to re-create the interface */
+ ifc_save_vparams(ifp_src, &vdata);
+ ifv = ifp_src->if_softc;
+
+ struct vlanparams vp = {
+ .parent = (ifv->ifv_trunk != NULL) ? ifv->ifv_trunk->parent : NULL,
+ .vid = ifv->ifv_vid,
+ .proto = ifv->ifv_proto,
+ };
+ error = if_clone_destroyif(ifc, ifp_src);
+ VLAN_XUNLOCK();
+ ifp_src = NULL;
+
+ if (error != 0)
+ return (error);
+
+ CURVNET_SET_QUIET(ifd->vnet);
+
+ VLAN_XLOCK();
+ error = vlan_create_specific(ifc, vdata.ifname,
+ vdata.unit, &vp, ifpp);
+ VLAN_XUNLOCK();
+ ifc_apply_vparams(*ifpp, &vdata);
+
+ CURVNET_RESTORE();
+
+ return (error);
+}
+
/*
* The ifp->if_init entry point for vlan(4) is a no-op.
*/
@@ -1519,7 +1588,7 @@
}
static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid,
+vlan_config_locked(struct ifvlan *ifv, struct ifnet *p, uint16_t vid,
uint16_t proto)
{
struct epoch_tracker et;
@@ -1549,8 +1618,6 @@
if (trunk->parent != p)
return (EBUSY);
- VLAN_XLOCK();
-
ifv->ifv_proto = proto;
if (ifv->ifv_vid != vid) {
@@ -1559,11 +1626,9 @@
ifv->ifv_vid = vid;
error = vlan_inshash(trunk, ifv);
}
- /* Will unlock */
- goto done;
+ return (error);
}
- VLAN_XLOCK();
if (p->if_vlantrunk == NULL) {
trunk = malloc(sizeof(struct ifvlantrunk),
M_VLAN, M_WAITOK | M_ZERO);
@@ -1581,8 +1646,8 @@
ifv->ifv_vid = vid; /* must set this before vlan_inshash() */
ifv->ifv_pcp = 0; /* Default: best effort delivery. */
error = vlan_inshash(trunk, ifv);
- if (error)
- goto done;
+ if (error != 0)
+ return (error);
ifv->ifv_proto = proto;
ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
ifv->ifv_mintu = ETHERMIN;
@@ -1677,7 +1742,15 @@
*/
(void)vlan_setmulti(ifp);
-done:
+ return (error);
+}
+
+static int
+vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid,
+ uint16_t proto)
+{
+ VLAN_XLOCK();
+ int error = vlan_config_locked(ifv, p, vid, proto);
if (error == 0)
EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid);
VLAN_XUNLOCK();

File Metadata

Mime Type
text/plain
Expires
Thu, May 1, 1:07 PM (17 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17881903
Default Alt Text
D36651.diff (24 KB)

Event Timeline