Virtualize lagg(4) cloner. This change fixes a panic when tearing down
if_lagg(4) interfaces which were cloned in a vnet jail. Sysctl nodes which are dynamically generated for each cloned interface (net.link.lagg.N.*) have been removed, and use_flowid and flowid_shift ifconfig(8) parameters have been added instead. Flags and per-interface statistics counters are displayed in "ifconfig -v". CR: D842
This commit is contained in:
parent
8b1af054e8
commit
939a050ad9
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272386
@ -28,7 +28,7 @@
|
||||
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 9, 2014
|
||||
.Dd October 1, 2014
|
||||
.Dt IFCONFIG 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -2331,9 +2331,16 @@ Remove the interface named by
|
||||
from the aggregation interface.
|
||||
.It Cm laggproto Ar proto
|
||||
Set the aggregation protocol.
|
||||
The default is failover.
|
||||
The available options are failover, lacp, loadbalance, roundrobin, broadcast
|
||||
and none.
|
||||
The default is
|
||||
.Li failover .
|
||||
The available options are
|
||||
.Li failover ,
|
||||
.Li lacp ,
|
||||
.Li loadbalance ,
|
||||
.Li roundrobin ,
|
||||
.Li broadcast
|
||||
and
|
||||
.Li none .
|
||||
.It Cm lagghash Ar option Ns Oo , Ns Ar option Oc
|
||||
Set the packet layers to hash for aggregation protocols which load balance.
|
||||
The default is
|
||||
@ -2348,6 +2355,34 @@ src/dst address for IPv4 or IPv6.
|
||||
.It Cm l4
|
||||
src/dst port for TCP/UDP/SCTP.
|
||||
.El
|
||||
.It Cm use_flowid
|
||||
Enable local hash computation for RSS hash on the interface.
|
||||
The
|
||||
.Li loadbalance
|
||||
and
|
||||
.Li lacp
|
||||
modes will use the RSS hash from the network card if available
|
||||
to avoid computing one, this may give poor traffic distribution
|
||||
if the hash is invalid or uses less of the protocol header information.
|
||||
.Cm use_flowid
|
||||
disables use of RSS hash from the network card.
|
||||
The default value can be set via the
|
||||
.Va net.link.lagg.default_use_flowid
|
||||
.Xr sysctl 8
|
||||
variable.
|
||||
.Li 0
|
||||
means
|
||||
.Dq disabled
|
||||
and
|
||||
.Li 1
|
||||
means
|
||||
.Dq enabled .
|
||||
.It Cm -use_flowid
|
||||
Disable local hash computation for RSS hash on the interface.
|
||||
.It Cm flowid_shift Ar number
|
||||
Set a shift parameter for RSS local hash computation.
|
||||
Hash is calculated by using flowid bits in a packet header mbuf
|
||||
which are shifted by the number of this parameter.
|
||||
.El
|
||||
.Pp
|
||||
The following parameters are specific to IP tunnel interfaces,
|
||||
|
@ -68,7 +68,7 @@ setlaggproto(const char *val, int d, int s, const struct afswtch *afp)
|
||||
bzero(&ra, sizeof(ra));
|
||||
ra.ra_proto = LAGG_PROTO_MAX;
|
||||
|
||||
for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) {
|
||||
for (i = 0; i < nitems(lpr); i++) {
|
||||
if (strcmp(val, lpr[i].lpr_name) == 0) {
|
||||
ra.ra_proto = lpr[i].lpr_proto;
|
||||
break;
|
||||
@ -82,6 +82,48 @@ setlaggproto(const char *val, int d, int s, const struct afswtch *afp)
|
||||
err(1, "SIOCSLAGG");
|
||||
}
|
||||
|
||||
static void
|
||||
setlaggflowidshift(const char *val, int d, int s, const struct afswtch *afp)
|
||||
{
|
||||
struct lagg_reqall ra;
|
||||
|
||||
bzero(&ra, sizeof(ra));
|
||||
ra.ra_opts = LAGG_OPT_FLOWIDSHIFT;
|
||||
strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
|
||||
ra.ra_flowid_shift = (int)strtol(val, NULL, 10);
|
||||
if (ra.ra_flowid_shift & ~LAGG_OPT_FLOWIDSHIFT_MASK)
|
||||
errx(1, "Invalid flowid_shift option: %s", val);
|
||||
|
||||
if (ioctl(s, SIOCSLAGG, &ra) != 0)
|
||||
err(1, "SIOCSLAGG");
|
||||
}
|
||||
|
||||
static void
|
||||
setlaggsetopt(const char *val, int d, int s, const struct afswtch *afp)
|
||||
{
|
||||
struct lagg_reqall ra;
|
||||
|
||||
bzero(&ra, sizeof(ra));
|
||||
ra.ra_opts = d;
|
||||
switch (ra.ra_opts) {
|
||||
case LAGG_OPT_USE_FLOWID:
|
||||
case -LAGG_OPT_USE_FLOWID:
|
||||
case LAGG_OPT_LACP_STRICT:
|
||||
case -LAGG_OPT_LACP_STRICT:
|
||||
case LAGG_OPT_LACP_TXTEST:
|
||||
case -LAGG_OPT_LACP_TXTEST:
|
||||
case LAGG_OPT_LACP_RXTEST:
|
||||
case -LAGG_OPT_LACP_RXTEST:
|
||||
break;
|
||||
default:
|
||||
err(1, "Invalid lagg option");
|
||||
}
|
||||
strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
|
||||
|
||||
if (ioctl(s, SIOCSLAGG, &ra) != 0)
|
||||
err(1, "SIOCSLAGG");
|
||||
}
|
||||
|
||||
static void
|
||||
setlagghash(const char *val, int d, int s, const struct afswtch *afp)
|
||||
{
|
||||
@ -169,7 +211,7 @@ lagg_status(int s)
|
||||
if (ioctl(s, SIOCGLAGG, &ra) == 0) {
|
||||
lp = (struct lacp_opreq *)&ra.ra_lacpreq;
|
||||
|
||||
for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) {
|
||||
for (i = 0; i < nitems(lpr); i++) {
|
||||
if (ra.ra_proto == lpr[i].lpr_proto) {
|
||||
proto = lpr[i].lpr_name;
|
||||
break;
|
||||
@ -197,9 +239,28 @@ lagg_status(int s)
|
||||
if (isport)
|
||||
printf(" laggdev %s", rp.rp_ifname);
|
||||
putchar('\n');
|
||||
if (verbose && ra.ra_proto == LAGG_PROTO_LACP)
|
||||
printf("\tlag id: %s\n",
|
||||
lacp_format_peer(lp, "\n\t\t "));
|
||||
if (verbose) {
|
||||
printf("\tlagg options:\n");
|
||||
printf("\t\tuse_flowid: %d\n",
|
||||
(ra.ra_opts & LAGG_OPT_USE_FLOWID) ? 1 : 0);
|
||||
printf("\t\tflowid_shift: %d\n", ra.ra_flowid_shift);
|
||||
switch (ra.ra_proto) {
|
||||
case LAGG_PROTO_LACP:
|
||||
printf("\t\tlacp_strict: %d\n",
|
||||
(ra.ra_opts & LAGG_OPT_LACP_STRICT) ? 1 : 0);
|
||||
printf("\t\tlacp_rxtest: %d\n",
|
||||
(ra.ra_opts & LAGG_OPT_LACP_RXTEST) ? 1 : 0);
|
||||
printf("\t\tlacp_txtest: %d\n",
|
||||
(ra.ra_opts & LAGG_OPT_LACP_TXTEST) ? 1 : 0);
|
||||
}
|
||||
printf("\tlagg statistics:\n");
|
||||
printf("\t\tactive ports: %d\n", ra.ra_active);
|
||||
printf("\t\tflapping: %u\n", ra.ra_flapping);
|
||||
if (ra.ra_proto == LAGG_PROTO_LACP) {
|
||||
printf("\tlag id: %s\n",
|
||||
lacp_format_peer(lp, "\n\t\t "));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ra.ra_ports; i++) {
|
||||
lp = (struct lacp_opreq *)&rpbuf[i].rp_lacpreq;
|
||||
@ -226,6 +287,15 @@ static struct cmd lagg_cmds[] = {
|
||||
DEF_CMD_ARG("-laggport", unsetlaggport),
|
||||
DEF_CMD_ARG("laggproto", setlaggproto),
|
||||
DEF_CMD_ARG("lagghash", setlagghash),
|
||||
DEF_CMD("use_flowid", LAGG_OPT_USE_FLOWID, setlaggsetopt),
|
||||
DEF_CMD("-use_flowid", -LAGG_OPT_USE_FLOWID, setlaggsetopt),
|
||||
DEF_CMD("lacp_strict", LAGG_OPT_LACP_STRICT, setlaggsetopt),
|
||||
DEF_CMD("-lacp_strict", -LAGG_OPT_LACP_STRICT, setlaggsetopt),
|
||||
DEF_CMD("lacp_txtest", LAGG_OPT_LACP_TXTEST, setlaggsetopt),
|
||||
DEF_CMD("-lacp_txtest", -LAGG_OPT_LACP_TXTEST, setlaggsetopt),
|
||||
DEF_CMD("lacp_rxtest", LAGG_OPT_LACP_RXTEST, setlaggsetopt),
|
||||
DEF_CMD("-lacp_rxtest", -LAGG_OPT_LACP_RXTEST, setlaggsetopt),
|
||||
DEF_CMD_ARG("flowid_shift", setlaggflowidshift),
|
||||
};
|
||||
static struct afswtch af_lagg = {
|
||||
.af_name = "af_lagg",
|
||||
|
@ -16,7 +16,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 23, 2012
|
||||
.Dd October 1, 2014
|
||||
.Dt LAGG 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -143,9 +143,9 @@ modes will use the RSS hash from the network card if available to avoid
|
||||
computing one, this may give poor traffic distribution if the hash is invalid
|
||||
or uses less of the protocol header information.
|
||||
Local hash computation can be forced per interface by setting the
|
||||
.Va net.link.lagg.X.use_flowid
|
||||
.Xr sysctl 8
|
||||
variable to zero where X is the interface number.
|
||||
.Cm use_flowid
|
||||
.Xr ifconfig 8
|
||||
flag.
|
||||
The default for new interfaces is set via the
|
||||
.Va net.link.lagg.default_use_flowid
|
||||
.Xr sysctl 8 .
|
||||
|
@ -190,14 +190,15 @@ static const char *lacp_format_portid(const struct lacp_portid *, char *,
|
||||
static void lacp_dprintf(const struct lacp_port *, const char *, ...)
|
||||
__attribute__((__format__(__printf__, 2, 3)));
|
||||
|
||||
static int lacp_debug = 0;
|
||||
static VNET_DEFINE(int, lacp_debug);
|
||||
#define V_lacp_debug VNET(lacp_debug)
|
||||
SYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD, 0, "ieee802.3ad");
|
||||
SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RWTUN,
|
||||
&lacp_debug, 0, "Enable LACP debug logging (1=debug, 2=trace)");
|
||||
SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RWTUN | CTLFLAG_VNET,
|
||||
&VNET_NAME(lacp_debug), 0, "Enable LACP debug logging (1=debug, 2=trace)");
|
||||
|
||||
#define LACP_DPRINTF(a) if (lacp_debug & 0x01) { lacp_dprintf a ; }
|
||||
#define LACP_TRACE(a) if (lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); }
|
||||
#define LACP_TPRINTF(a) if (lacp_debug & 0x04) { lacp_dprintf a ; }
|
||||
#define LACP_DPRINTF(a) if (V_lacp_debug & 0x01) { lacp_dprintf a ; }
|
||||
#define LACP_TRACE(a) if (V_lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); }
|
||||
#define LACP_TPRINTF(a) if (V_lacp_debug & 0x04) { lacp_dprintf a ; }
|
||||
|
||||
/*
|
||||
* partner administration variables.
|
||||
@ -300,7 +301,7 @@ lacp_pdu_input(struct lacp_port *lp, struct mbuf *m)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (lacp_debug > 0) {
|
||||
if (V_lacp_debug > 0) {
|
||||
lacp_dprintf(lp, "lacpdu receive\n");
|
||||
lacp_dump_lacpdu(du);
|
||||
}
|
||||
@ -385,7 +386,7 @@ lacp_xmit_lacpdu(struct lacp_port *lp)
|
||||
sizeof(du->ldu_collector));
|
||||
du->ldu_collector.lci_maxdelay = 0;
|
||||
|
||||
if (lacp_debug > 0) {
|
||||
if (V_lacp_debug > 0) {
|
||||
lacp_dprintf(lp, "lacpdu transmit\n");
|
||||
lacp_dump_lacpdu(du);
|
||||
}
|
||||
@ -497,12 +498,14 @@ lacp_tick(void *arg)
|
||||
if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0)
|
||||
continue;
|
||||
|
||||
CURVNET_SET(lp->lp_ifp->if_vnet);
|
||||
lacp_run_timers(lp);
|
||||
|
||||
lacp_select(lp);
|
||||
lacp_sm_mux(lp);
|
||||
lacp_sm_tx(lp);
|
||||
lacp_sm_ptx_tx_schedule(lp);
|
||||
CURVNET_RESTORE();
|
||||
}
|
||||
callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc);
|
||||
}
|
||||
@ -747,48 +750,10 @@ lacp_transit_expire(void *vp)
|
||||
lsc->lsc_suppress_distributing = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
lacp_attach_sysctl(struct lacp_softc *lsc, struct sysctl_oid *p_oid)
|
||||
{
|
||||
struct lagg_softc *sc = lsc->lsc_softc;
|
||||
|
||||
SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(p_oid), OID_AUTO,
|
||||
"lacp_strict_mode",
|
||||
CTLFLAG_RW,
|
||||
&lsc->lsc_strict_mode,
|
||||
lsc->lsc_strict_mode,
|
||||
"Enable LACP strict mode");
|
||||
}
|
||||
|
||||
static void
|
||||
lacp_attach_sysctl_debug(struct lacp_softc *lsc, struct sysctl_oid *p_oid)
|
||||
{
|
||||
struct lagg_softc *sc = lsc->lsc_softc;
|
||||
struct sysctl_oid *oid;
|
||||
|
||||
/* Create a child of the parent lagg interface */
|
||||
oid = SYSCTL_ADD_NODE(&sc->ctx, SYSCTL_CHILDREN(p_oid),
|
||||
OID_AUTO, "debug", CTLFLAG_RD, NULL, "DEBUG");
|
||||
|
||||
SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"rx_test",
|
||||
CTLFLAG_RW,
|
||||
&lsc->lsc_debug.lsc_rx_test,
|
||||
lsc->lsc_debug.lsc_rx_test,
|
||||
"Bitmap of if_dunit entries to drop RX frames for");
|
||||
SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"tx_test",
|
||||
CTLFLAG_RW,
|
||||
&lsc->lsc_debug.lsc_tx_test,
|
||||
lsc->lsc_debug.lsc_tx_test,
|
||||
"Bitmap of if_dunit entries to drop TX frames for");
|
||||
}
|
||||
|
||||
void
|
||||
lacp_attach(struct lagg_softc *sc)
|
||||
{
|
||||
struct lacp_softc *lsc;
|
||||
struct sysctl_oid *oid;
|
||||
|
||||
lsc = malloc(sizeof(struct lacp_softc), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
|
||||
@ -802,14 +767,6 @@ lacp_attach(struct lagg_softc *sc)
|
||||
TAILQ_INIT(&lsc->lsc_aggregators);
|
||||
LIST_INIT(&lsc->lsc_ports);
|
||||
|
||||
/* Create a child of the parent lagg interface */
|
||||
oid = SYSCTL_ADD_NODE(&sc->ctx, SYSCTL_CHILDREN(sc->sc_oid),
|
||||
OID_AUTO, "lacp", CTLFLAG_RD, NULL, "LACP");
|
||||
|
||||
/* Attach sysctl nodes */
|
||||
lacp_attach_sysctl(lsc, oid);
|
||||
lacp_attach_sysctl_debug(lsc, oid);
|
||||
|
||||
callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0);
|
||||
callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0);
|
||||
|
||||
@ -875,7 +832,7 @@ lacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (sc->use_flowid && (m->m_flags & M_FLOWID))
|
||||
if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && (m->m_flags & M_FLOWID))
|
||||
hash = m->m_pkthdr.flowid >> sc->flowid_shift;
|
||||
else
|
||||
hash = lagg_hashmbuf(sc, m, lsc->lsc_hashkey);
|
||||
@ -1374,7 +1331,7 @@ lacp_sm_mux(struct lacp_port *lp)
|
||||
enum lacp_selected selected = lp->lp_selected;
|
||||
struct lacp_aggregator *la;
|
||||
|
||||
if (lacp_debug > 1)
|
||||
if (V_lacp_debug > 1)
|
||||
lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, "
|
||||
"p_sync= 0x%x, p_collecting= 0x%x\n", __func__,
|
||||
lp->lp_mux_state, selected, p_sync, p_collecting);
|
||||
|
@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/vnet.h>
|
||||
|
||||
#if defined(INET) || defined(INET6)
|
||||
#include <netinet/in.h>
|
||||
@ -81,13 +82,21 @@ static struct {
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
SLIST_HEAD(__trhead, lagg_softc) lagg_list; /* list of laggs */
|
||||
static struct mtx lagg_list_mtx;
|
||||
VNET_DEFINE(SLIST_HEAD(__trhead, lagg_softc), lagg_list); /* list of laggs */
|
||||
#define V_lagg_list VNET(lagg_list)
|
||||
static VNET_DEFINE(struct mtx, lagg_list_mtx);
|
||||
#define V_lagg_list_mtx VNET(lagg_list_mtx)
|
||||
#define LAGG_LIST_LOCK_INIT(x) mtx_init(&V_lagg_list_mtx, \
|
||||
"if_lagg list", NULL, MTX_DEF)
|
||||
#define LAGG_LIST_LOCK_DESTROY(x) mtx_destroy(&V_lagg_list_mtx)
|
||||
#define LAGG_LIST_LOCK(x) mtx_lock(&V_lagg_list_mtx)
|
||||
#define LAGG_LIST_UNLOCK(x) mtx_unlock(&V_lagg_list_mtx)
|
||||
eventhandler_tag lagg_detach_cookie = NULL;
|
||||
|
||||
static int lagg_clone_create(struct if_clone *, int, caddr_t);
|
||||
static void lagg_clone_destroy(struct ifnet *);
|
||||
static struct if_clone *lagg_cloner;
|
||||
static VNET_DEFINE(struct if_clone *, lagg_cloner);
|
||||
#define V_lagg_cloner VNET(lagg_cloner)
|
||||
static const char laggname[] = "lagg";
|
||||
|
||||
static void lagg_lladdr(struct lagg_softc *, uint8_t *);
|
||||
@ -123,7 +132,6 @@ static void lagg_media_status(struct ifnet *, struct ifmediareq *);
|
||||
static struct lagg_port *lagg_link_active(struct lagg_softc *,
|
||||
struct lagg_port *);
|
||||
static const void *lagg_gethdr(struct mbuf *, u_int, u_int, void *);
|
||||
static int lagg_sysctl_active(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
/* Simple round robin */
|
||||
static void lagg_rr_attach(struct lagg_softc *);
|
||||
@ -232,29 +240,55 @@ SYSCTL_DECL(_net_link);
|
||||
SYSCTL_NODE(_net_link, OID_AUTO, lagg, CTLFLAG_RW, 0,
|
||||
"Link Aggregation");
|
||||
|
||||
static int lagg_failover_rx_all = 0; /* Allow input on any failover links */
|
||||
SYSCTL_INT(_net_link_lagg, OID_AUTO, failover_rx_all, CTLFLAG_RW,
|
||||
&lagg_failover_rx_all, 0,
|
||||
/* Allow input on any failover links */
|
||||
static VNET_DEFINE(int, lagg_failover_rx_all);
|
||||
#define V_lagg_failover_rx_all VNET(lagg_failover_rx_all)
|
||||
SYSCTL_INT(_net_link_lagg, OID_AUTO, failover_rx_all, CTLFLAG_RW | CTLFLAG_VNET,
|
||||
&VNET_NAME(lagg_failover_rx_all), 0,
|
||||
"Accept input from any interface in a failover lagg");
|
||||
static int def_use_flowid = 1; /* Default value for using M_FLOWID */
|
||||
|
||||
/* Default value for using M_FLOWID */
|
||||
static VNET_DEFINE(int, def_use_flowid) = 1;
|
||||
#define V_def_use_flowid VNET(def_use_flowid)
|
||||
SYSCTL_INT(_net_link_lagg, OID_AUTO, default_use_flowid, CTLFLAG_RWTUN,
|
||||
&def_use_flowid, 0,
|
||||
&VNET_NAME(def_use_flowid), 0,
|
||||
"Default setting for using flow id for load sharing");
|
||||
static int def_flowid_shift = 16; /* Default value for using M_FLOWID */
|
||||
|
||||
/* Default value for using M_FLOWID */
|
||||
static VNET_DEFINE(int, def_flowid_shift) = 16;
|
||||
#define V_def_flowid_shift VNET(def_flowid_shift)
|
||||
SYSCTL_INT(_net_link_lagg, OID_AUTO, default_flowid_shift, CTLFLAG_RWTUN,
|
||||
&def_flowid_shift, 0,
|
||||
&VNET_NAME(def_flowid_shift), 0,
|
||||
"Default setting for flowid shift for load sharing");
|
||||
|
||||
static void
|
||||
vnet_lagg_init(const void *unused __unused)
|
||||
{
|
||||
|
||||
LAGG_LIST_LOCK_INIT();
|
||||
SLIST_INIT(&V_lagg_list);
|
||||
V_lagg_cloner = if_clone_simple(laggname, lagg_clone_create,
|
||||
lagg_clone_destroy, 0);
|
||||
}
|
||||
VNET_SYSINIT(vnet_lagg_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
|
||||
vnet_lagg_init, NULL);
|
||||
|
||||
static void
|
||||
vnet_lagg_uninit(const void *unused __unused)
|
||||
{
|
||||
|
||||
if_clone_detach(V_lagg_cloner);
|
||||
LAGG_LIST_LOCK_DESTROY();
|
||||
}
|
||||
VNET_SYSUNINIT(vnet_lagg_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
|
||||
vnet_lagg_uninit, NULL);
|
||||
|
||||
static int
|
||||
lagg_modevent(module_t mod, int type, void *data)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
mtx_init(&lagg_list_mtx, "if_lagg list", NULL, MTX_DEF);
|
||||
SLIST_INIT(&lagg_list);
|
||||
lagg_cloner = if_clone_simple(laggname, lagg_clone_create,
|
||||
lagg_clone_destroy, 0);
|
||||
lagg_input_p = lagg_input;
|
||||
lagg_linkstate_p = lagg_port_state;
|
||||
lagg_detach_cookie = EVENTHANDLER_REGISTER(
|
||||
@ -264,10 +298,8 @@ lagg_modevent(module_t mod, int type, void *data)
|
||||
case MOD_UNLOAD:
|
||||
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
|
||||
lagg_detach_cookie);
|
||||
if_clone_detach(lagg_cloner);
|
||||
lagg_input_p = NULL;
|
||||
lagg_linkstate_p = NULL;
|
||||
mtx_destroy(&lagg_list_mtx);
|
||||
break;
|
||||
default:
|
||||
return (EOPNOTSUPP);
|
||||
@ -445,8 +477,6 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
struct lagg_softc *sc;
|
||||
struct ifnet *ifp;
|
||||
static const u_char eaddr[6]; /* 00:00:00:00:00:00 */
|
||||
struct sysctl_oid *oid;
|
||||
char num[14]; /* sufficient for 32 bits */
|
||||
|
||||
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
|
||||
ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
|
||||
@ -455,29 +485,10 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
sysctl_ctx_init(&sc->ctx);
|
||||
snprintf(num, sizeof(num), "%u", unit);
|
||||
sc->use_flowid = def_use_flowid;
|
||||
sc->flowid_shift = def_flowid_shift;
|
||||
sc->sc_oid = oid = SYSCTL_ADD_NODE(&sc->ctx,
|
||||
&SYSCTL_NODE_CHILDREN(_net_link, lagg),
|
||||
OID_AUTO, num, CTLFLAG_RD, NULL, "");
|
||||
SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"use_flowid", CTLTYPE_INT|CTLFLAG_RW, &sc->use_flowid,
|
||||
sc->use_flowid, "Use flow id for load sharing");
|
||||
SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"flowid_shift", CTLTYPE_INT|CTLFLAG_RW, &sc->flowid_shift,
|
||||
sc->flowid_shift,
|
||||
"Shift flowid bits to prevent multiqueue collisions");
|
||||
SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"count", CTLTYPE_INT|CTLFLAG_RD, &sc->sc_count, sc->sc_count,
|
||||
"Total number of ports");
|
||||
SYSCTL_ADD_PROC(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"active", CTLTYPE_INT|CTLFLAG_RD, sc, 0, lagg_sysctl_active,
|
||||
"I", "Total number of active ports");
|
||||
SYSCTL_ADD_INT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
|
||||
"flapping", CTLTYPE_INT|CTLFLAG_RD, &sc->sc_flapping,
|
||||
sc->sc_flapping, "Total number of port change events");
|
||||
if (V_def_use_flowid)
|
||||
sc->sc_opts |= LAGG_OPT_USE_FLOWID;
|
||||
sc->flowid_shift = V_def_flowid_shift;
|
||||
|
||||
/* Hash all layers by default */
|
||||
sc->sc_flags = LAGG_F_HASHL2|LAGG_F_HASHL3|LAGG_F_HASHL4;
|
||||
|
||||
@ -515,9 +526,9 @@ lagg_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
lagg_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST);
|
||||
|
||||
/* Insert into the global list of laggs */
|
||||
mtx_lock(&lagg_list_mtx);
|
||||
SLIST_INSERT_HEAD(&lagg_list, sc, sc_entries);
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
LAGG_LIST_LOCK();
|
||||
SLIST_INSERT_HEAD(&V_lagg_list, sc, sc_entries);
|
||||
LAGG_LIST_UNLOCK();
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -542,14 +553,13 @@ lagg_clone_destroy(struct ifnet *ifp)
|
||||
/* Unhook the aggregation protocol */
|
||||
lagg_proto_detach(sc);
|
||||
|
||||
sysctl_ctx_free(&sc->ctx);
|
||||
ifmedia_removeall(&sc->sc_media);
|
||||
ether_ifdetach(ifp);
|
||||
if_free(ifp);
|
||||
|
||||
mtx_lock(&lagg_list_mtx);
|
||||
SLIST_REMOVE(&lagg_list, sc, lagg_softc, sc_entries);
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
LAGG_LIST_LOCK();
|
||||
SLIST_REMOVE(&V_lagg_list, sc, lagg_softc, sc_entries);
|
||||
LAGG_LIST_UNLOCK();
|
||||
|
||||
taskqueue_drain(taskqueue_swi, &sc->sc_lladdr_task);
|
||||
LAGG_LOCK_DESTROY(sc);
|
||||
@ -752,10 +762,10 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
|
||||
return (ENOMEM);
|
||||
|
||||
/* Check if port is a stacked lagg */
|
||||
mtx_lock(&lagg_list_mtx);
|
||||
SLIST_FOREACH(sc_ptr, &lagg_list, sc_entries) {
|
||||
LAGG_LIST_LOCK();
|
||||
SLIST_FOREACH(sc_ptr, &V_lagg_list, sc_entries) {
|
||||
if (ifp == sc_ptr->sc_ifp) {
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
LAGG_LIST_UNLOCK();
|
||||
free(lp, M_DEVBUF);
|
||||
return (EINVAL);
|
||||
/* XXX disable stacking for the moment, its untested */
|
||||
@ -763,14 +773,14 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
|
||||
lp->lp_flags |= LAGG_PORT_STACK;
|
||||
if (lagg_port_checkstacking(sc_ptr) >=
|
||||
LAGG_MAX_STACKING) {
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
LAGG_LIST_UNLOCK();
|
||||
free(lp, M_DEVBUF);
|
||||
return (E2BIG);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
mtx_unlock(&lagg_list_mtx);
|
||||
LAGG_LIST_UNLOCK();
|
||||
|
||||
/* Change the interface type */
|
||||
lp->lp_iftype = ifp->if_type;
|
||||
@ -1205,6 +1215,31 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
LAGG_RLOCK(sc, &tracker);
|
||||
ra->ra_proto = sc->sc_proto;
|
||||
lagg_proto_request(sc, &ra->ra_psc);
|
||||
ra->ra_opts = sc->sc_opts;
|
||||
if (sc->sc_proto == LAGG_PROTO_LACP) {
|
||||
struct lacp_softc *lsc;
|
||||
|
||||
lsc = (struct lacp_softc *)sc->sc_psc;
|
||||
if (lsc->lsc_debug.lsc_tx_test != 0)
|
||||
ra->ra_opts |= LAGG_OPT_LACP_TXTEST;
|
||||
if (lsc->lsc_debug.lsc_rx_test != 0)
|
||||
ra->ra_opts |= LAGG_OPT_LACP_RXTEST;
|
||||
if (lsc->lsc_strict_mode != 0)
|
||||
ra->ra_opts |= LAGG_OPT_LACP_STRICT;
|
||||
|
||||
ra->ra_active = sc->sc_active;
|
||||
} else {
|
||||
/*
|
||||
* LACP tracks active links automatically,
|
||||
* the others do not.
|
||||
*/
|
||||
ra->ra_active = 0;
|
||||
SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
|
||||
ra->ra_active += LAGG_PORTACTIVE(lp);
|
||||
}
|
||||
ra->ra_flapping = sc->sc_flapping;
|
||||
ra->ra_flowid_shift = sc->flowid_shift;
|
||||
|
||||
count = 0;
|
||||
buf = outbuf;
|
||||
len = min(ra->ra_size, buflen);
|
||||
@ -1225,9 +1260,88 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
free(outbuf, M_TEMP);
|
||||
break;
|
||||
case SIOCSLAGG:
|
||||
/*
|
||||
* Set options or protocol depending on
|
||||
* ra->ra_opts and ra->ra_proto.
|
||||
*/
|
||||
error = priv_check(td, PRIV_NET_LAGG);
|
||||
if (error)
|
||||
break;
|
||||
if (ra->ra_opts != 0) {
|
||||
/*
|
||||
* Set options. LACP options are stored in sc->sc_psc,
|
||||
* not in sc_opts.
|
||||
*/
|
||||
int valid, lacp;
|
||||
|
||||
switch (ra->ra_opts) {
|
||||
case LAGG_OPT_USE_FLOWID:
|
||||
case -LAGG_OPT_USE_FLOWID:
|
||||
case LAGG_OPT_FLOWIDSHIFT:
|
||||
valid = 1;
|
||||
lacp = 0;
|
||||
break;
|
||||
case LAGG_OPT_LACP_TXTEST:
|
||||
case -LAGG_OPT_LACP_TXTEST:
|
||||
case LAGG_OPT_LACP_RXTEST:
|
||||
case -LAGG_OPT_LACP_RXTEST:
|
||||
case LAGG_OPT_LACP_STRICT:
|
||||
case -LAGG_OPT_LACP_STRICT:
|
||||
valid = lacp = 1;
|
||||
break;
|
||||
default:
|
||||
valid = lacp = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LAGG_WLOCK(sc);
|
||||
if (valid == 0 ||
|
||||
(lacp == 1 && sc->sc_proto != LAGG_PROTO_LACP)) {
|
||||
/* Invalid combination of options specified. */
|
||||
error = EINVAL;
|
||||
LAGG_WUNLOCK(sc);
|
||||
break; /* Return from SIOCSLAGG. */
|
||||
}
|
||||
/*
|
||||
* Store new options into sc->sc_opts except for
|
||||
* FLOWIDSHIFT and LACP options.
|
||||
*/
|
||||
if (lacp == 0) {
|
||||
if (ra->ra_opts == LAGG_OPT_FLOWIDSHIFT)
|
||||
sc->flowid_shift = ra->ra_flowid_shift;
|
||||
else if (ra->ra_opts > 0)
|
||||
sc->sc_opts |= ra->ra_opts;
|
||||
else
|
||||
sc->sc_opts &= ~ra->ra_opts;
|
||||
} else {
|
||||
struct lacp_softc *lsc;
|
||||
|
||||
lsc = (struct lacp_softc *)sc->sc_psc;
|
||||
|
||||
switch (ra->ra_opts) {
|
||||
case LAGG_OPT_LACP_TXTEST:
|
||||
lsc->lsc_debug.lsc_tx_test = 1;
|
||||
break;
|
||||
case -LAGG_OPT_LACP_TXTEST:
|
||||
lsc->lsc_debug.lsc_tx_test = 0;
|
||||
break;
|
||||
case LAGG_OPT_LACP_RXTEST:
|
||||
lsc->lsc_debug.lsc_rx_test = 1;
|
||||
break;
|
||||
case -LAGG_OPT_LACP_RXTEST:
|
||||
lsc->lsc_debug.lsc_rx_test = 0;
|
||||
break;
|
||||
case LAGG_OPT_LACP_STRICT:
|
||||
lsc->lsc_strict_mode = 1;
|
||||
break;
|
||||
case -LAGG_OPT_LACP_STRICT:
|
||||
lsc->lsc_strict_mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LAGG_WUNLOCK(sc);
|
||||
break; /* Return from SIOCSLAGG. */
|
||||
}
|
||||
if (ra->ra_proto < 1 || ra->ra_proto >= LAGG_PROTO_MAX) {
|
||||
error = EPROTONOSUPPORT;
|
||||
break;
|
||||
@ -1687,27 +1801,6 @@ lagg_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
|
||||
return (mtod(m, char *) + off);
|
||||
}
|
||||
|
||||
static int
|
||||
lagg_sysctl_active(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct lagg_softc *sc = (struct lagg_softc *)arg1;
|
||||
struct lagg_port *lp;
|
||||
int error;
|
||||
|
||||
/* LACP tracks active links automatically, the others do not */
|
||||
if (sc->sc_proto != LAGG_PROTO_LACP) {
|
||||
sc->sc_active = 0;
|
||||
SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
|
||||
sc->sc_active += LAGG_PORTACTIVE(lp);
|
||||
}
|
||||
|
||||
error = sysctl_handle_int(oidp, &sc->sc_active, 0, req);
|
||||
if ((error) || (req->newptr == NULL))
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
lagg_hashmbuf(struct lagg_softc *sc, struct mbuf *m, uint32_t key)
|
||||
{
|
||||
@ -1948,7 +2041,7 @@ lagg_fail_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
|
||||
struct ifnet *ifp = sc->sc_ifp;
|
||||
struct lagg_port *tmp_tp;
|
||||
|
||||
if (lp == sc->sc_primary || lagg_failover_rx_all) {
|
||||
if (lp == sc->sc_primary || V_lagg_failover_rx_all) {
|
||||
m->m_pkthdr.rcvif = ifp;
|
||||
return (m);
|
||||
}
|
||||
@ -2043,7 +2136,7 @@ lagg_lb_start(struct lagg_softc *sc, struct mbuf *m)
|
||||
struct lagg_port *lp = NULL;
|
||||
uint32_t p = 0;
|
||||
|
||||
if (sc->use_flowid && (m->m_flags & M_FLOWID))
|
||||
if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && (m->m_flags & M_FLOWID))
|
||||
p = m->m_pkthdr.flowid >> sc->flowid_shift;
|
||||
else
|
||||
p = lagg_hashmbuf(sc, m, lb->lb_key);
|
||||
|
@ -125,6 +125,19 @@ struct lagg_reqall {
|
||||
struct lacp_opreq rpsc_lacp;
|
||||
} ra_psc;
|
||||
#define ra_lacpreq ra_psc.rpsc_lacp
|
||||
int ra_opts; /* Option bitmap */
|
||||
#define LAGG_OPT_NONE 0x00
|
||||
#define LAGG_OPT_USE_FLOWID 0x01 /* use M_FLOWID */
|
||||
/* Pseudo flags which are used in ra_opts but not stored into sc_opts. */
|
||||
#define LAGG_OPT_FLOWIDSHIFT 0x02 /* Set flowid */
|
||||
#define LAGG_OPT_FLOWIDSHIFT_MASK 0x1f /* flowid is uint32_t */
|
||||
#define LAGG_OPT_LACP_STRICT 0x10 /* LACP strict mode */
|
||||
#define LAGG_OPT_LACP_TXTEST 0x20 /* LACP debug: txtest */
|
||||
#define LAGG_OPT_LACP_RXTEST 0x40 /* LACP debug: rxtest */
|
||||
u_int ra_count; /* number of ports */
|
||||
u_int ra_active; /* active port count */
|
||||
u_int ra_flapping; /* number of flapping */
|
||||
int ra_flowid_shift; /* shift the flowid */
|
||||
};
|
||||
|
||||
#define SIOCGLAGG _IOWR('i', 143, struct lagg_reqall)
|
||||
@ -212,9 +225,7 @@ struct lagg_softc {
|
||||
eventhandler_tag vlan_attach;
|
||||
eventhandler_tag vlan_detach;
|
||||
struct callout sc_callout;
|
||||
struct sysctl_ctx_list ctx; /* sysctl variables */
|
||||
struct sysctl_oid *sc_oid; /* sysctl tree oid */
|
||||
int use_flowid; /* use M_FLOWID */
|
||||
u_int sc_opts;
|
||||
int flowid_shift; /* shift the flowid */
|
||||
struct lagg_counters detached_counters; /* detached ports sum */
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user