Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115928668
D36651.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D36651.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D36651: if_move: create new ifp instances on each vmove
Attached
Detach File
Event Timeline
Log In to Comment