VLOG_DEFINE_THIS_MODULE(bond);
-COVERAGE_DEFINE(bond_process_lacp);
-
/* Bit-mask for hashing a flow down to a bucket.
* There are (BOND_MASK + 1) buckets. */
#define BOND_MASK 0xff
bool stb_need_sort; /* True if stb_slaves is not sorted. */
/* LACP. */
- struct lacp *lacp; /* LACP object. NULL if LACP is disabled. */
+ const struct lacp *lacp; /* Client provided LACP handle. */
/* Monitoring. */
enum bond_detect_mode detect; /* Link status mode, one of BLSM_*. */
free(bond->hash);
- lacp_destroy(bond->lacp);
-
netdev_monitor_destroy(bond->monitor);
free(bond->name);
hmap_insert(&all_bonds, &bond->hmap_node, hash_string(bond->name, 0));
}
+ bond->lacp = s->lacp;
bond->detect = s->detect;
bond->miimon_interval = s->miimon_interval;
bond->updelay = s->up_delay;
}
}
- if (s->lacp) {
- if (!bond->lacp) {
- bond->lacp = lacp_create();
- }
- lacp_configure(bond->lacp, s->lacp);
- } else {
- lacp_destroy(bond->lacp);
- bond->lacp = NULL;
- }
-
if (s->fake_iface) {
if (bond->next_fake_iface_update == LLONG_MAX) {
bond->next_fake_iface_update = time_msec();
* by the client, so the client must not close it before either unregistering
* 'slave_' or destroying 'bond'.
*
- * If 'bond' has a LACP configuration then 'lacp_settings' must point to LACP
- * settings for 'slave_'; otherwise 'lacp_settings' is ignored.
- */
+ * If 'bond' has a LACP configuration then 'slave_' should be the same handle
+ * used in the LACP module. */
void
-bond_slave_register(struct bond *bond, void *slave_, struct netdev *netdev,
- const struct lacp_slave_settings *lacp_settings)
+bond_slave_register(struct bond *bond, void *slave_, struct netdev *netdev)
{
struct bond_slave *slave = bond_slave_lookup(bond, slave_);
slave->netdev = netdev;
free(slave->name);
slave->name = xstrdup(netdev_get_name(netdev));
-
- if (bond->lacp) {
- assert(lacp_settings != NULL);
- lacp_slave_register(bond->lacp, slave, lacp_settings);
- }
}
/* Unregisters 'slave_' from 'bond'. If 'bond' does not contain such a slave
}
}
-/* Callback for lacp_run(). */
-static void
-bond_send_pdu_cb(void *slave_, const struct lacp_pdu *pdu)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
- struct bond_slave *slave = slave_;
- uint8_t ea[ETH_ADDR_LEN];
- int error;
-
- error = netdev_get_etheraddr(slave->netdev, ea);
- if (!error) {
- struct lacp_pdu *packet_pdu;
- struct ofpbuf packet;
-
- ofpbuf_init(&packet, 0);
- packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
- sizeof *packet_pdu);
- *packet_pdu = *pdu;
- error = netdev_send(slave->netdev, &packet);
- if (error) {
- VLOG_WARN_RL(&rl, "bond %s: sending LACP PDU on slave %s failed "
- "(%s)",
- slave->bond->name, slave->name, strerror(error));
- }
- ofpbuf_uninit(&packet);
- } else {
- VLOG_ERR_RL(&rl, "bond %s: cannot obtain Ethernet address of slave "
- "%s (%s)",
- slave->bond->name, slave->name, strerror(error));
- }
-}
-
/* Performs periodic maintenance on 'bond'. The caller must provide 'tags' to
* allow tagged flows to be invalidated.
*
bond->miimon_next_update = time_msec() + bond->miimon_interval;
}
- /* Update LACP. */
- if (bond->lacp) {
- lacp_run(bond->lacp, bond_send_pdu_cb);
- }
-
/* Enable slaves based on link status and LACP feedback. */
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
bond_link_status_update(slave, tags);
return NULL;
}
}
-
-/* Processes LACP packet 'packet', which was received on 'slave_' within
- * 'bond'.
- *
- * The client should use this function to pass along LACP messages received on
- * any of 'bond''s slaves. */
-void
-bond_process_lacp(struct bond *bond, void *slave_, const struct ofpbuf *packet)
-{
- if (bond->lacp) {
- struct bond_slave *slave = bond_slave_lookup(bond, slave_);
- const struct lacp_pdu *pdu = parse_lacp_packet(packet);
- if (slave && pdu) {
- COVERAGE_INC(bond_process_lacp);
- lacp_process_pdu(bond->lacp, slave, pdu);
- }
- }
-}
\f
/* Rebalancing. */
void
bond_init(void)
{
- lacp_init();
-
unixctl_command_register("bond/list", bond_unixctl_list, NULL);
unixctl_command_register("bond/show", bond_unixctl_show, NULL);
unixctl_command_register("bond/migrate", bond_unixctl_migrate, NULL);
const struct bond_slave *const *bp = b_;
const struct bond_slave *a = *ap;
const struct bond_slave *b = *bp;
- struct lacp *lacp = a->bond->lacp;
+ const struct lacp *lacp = a->bond->lacp;
int a_id, b_id;
if (lacp) {
- a_id = lacp_slave_get_port_id(lacp, a);
- b_id = lacp_slave_get_port_id(lacp, b);
+ a_id = lacp_slave_get_port_id(lacp, a->aux);
+ b_id = lacp_slave_get_port_id(lacp, b->aux);
} else {
a_id = netdev_get_ifindex(a->netdev);
b_id = netdev_get_ifindex(b->netdev);
struct bond *bond = slave->bond;
bool up;
- up = slave->up && lacp_slave_may_enable(bond->lacp, slave);
+ up = slave->up && lacp_slave_may_enable(bond->lacp, slave->aux);
if ((up == slave->enabled) != (slave->delay_expires == LLONG_MAX)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
VLOG_INFO_RL(&rl, "interface %s: link state %s",
best = NULL;
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
if (slave->delay_expires != LLONG_MAX
- && lacp_slave_may_enable(bond->lacp, slave)
+ && lacp_slave_may_enable(bond->lacp, slave->aux)
&& (!best || slave->delay_expires < best->delay_expires)) {
best = slave;
}
#include "tag.h"
struct flow;
-struct lacp_slave_settings;
struct netdev;
struct ofpbuf;
/* Legacy compatibility. */
bool fake_iface; /* Update fake stats for netdev 'name'? */
- /* LACP. */
- struct lacp_settings *lacp; /* NULL to disable LACP. */
+ const struct lacp *lacp; /* LACP for this bond. May be NULL. */
};
/* Program startup. */
void bond_destroy(struct bond *);
bool bond_reconfigure(struct bond *, const struct bond_settings *);
-void bond_slave_register(struct bond *, void *slave_, struct netdev *,
- const struct lacp_slave_settings *);
+void bond_slave_register(struct bond *, void *slave_, struct netdev *);
void bond_slave_unregister(struct bond *, const void *slave);
void bond_run(struct bond *, struct tag_set *);
tag_type *);
void *bond_choose_output_slave(struct bond *,
const struct flow *, uint16_t vlan, tag_type *);
-void bond_process_lacp(struct bond *, void *slave,
- const struct ofpbuf *packet);
/* Rebalancing. */
void bond_account(struct bond *, const struct flow *, uint16_t vlan,
* A bridge port for bonding has at least 2 interfaces. */
struct list ifaces; /* List of "struct iface"s. */
+ struct lacp *lacp; /* NULL if LACP is not enabled. */
+
/* Bonding info. */
struct bond *bond;
static struct iface *port_get_an_iface(const struct port *);
static struct port *port_from_dp_ifidx(const struct bridge *,
uint16_t dp_ifidx);
+static void port_reconfigure_lacp(struct port *);
static void port_reconfigure_bond(struct port *);
static void port_send_learning_packets(struct port *);
NULL);
unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
NULL);
+ lacp_init();
bond_init();
}
HMAP_FOR_EACH (port, hmap_node, &br->ports) {
struct iface *iface;
+ port_reconfigure_lacp(port);
port_reconfigure_bond(port);
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
iface = iface_from_dp_ifidx(br, flow->in_port);
if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- if (iface && iface->port->bond && packet) {
- bond_process_lacp(iface->port->bond, iface, packet);
+ if (iface && iface->port->lacp && packet) {
+ const struct lacp_pdu *pdu = parse_lacp_packet(packet);
+ if (pdu) {
+ lacp_process_pdu(iface->port->lacp, iface, pdu);
+ }
}
return false;
}
\f
/* Port functions. */
+static void
+lacp_send_pdu_cb(void *iface_, const struct lacp_pdu *pdu)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
+ struct iface *iface = iface_;
+ uint8_t ea[ETH_ADDR_LEN];
+ int error;
+
+ error = netdev_get_etheraddr(iface->netdev, ea);
+ if (!error) {
+ struct lacp_pdu *packet_pdu;
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, 0);
+ packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP,
+ sizeof *packet_pdu);
+ *packet_pdu = *pdu;
+ error = netdev_send(iface->netdev, &packet);
+ if (error) {
+ VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
+ "(%s)", iface->port->name, iface->name,
+ strerror(error));
+ }
+ ofpbuf_uninit(&packet);
+ } else {
+ VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
+ "%s (%s)", iface->port->name, iface->name,
+ strerror(error));
+ }
+}
+
static void
port_run(struct port *port)
{
+ if (port->lacp) {
+ lacp_run(port->lacp, lacp_send_pdu_cb);
+ }
+
if (port->bond) {
bond_run(port->bond,
ofproto_get_revalidate_set(port->bridge->ofproto));
static void
port_wait(struct port *port)
{
+ if (port->lacp) {
+ lacp_wait(port->lacp);
+ }
+
if (port->bond) {
bond_wait(port->bond);
}
VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name);
+ lacp_destroy(port->lacp);
port_flush_macs(port);
bitmap_free(port->trunks);
}
}
-static struct lacp_settings *
-port_reconfigure_bond_lacp(struct port *port, struct lacp_settings *s)
-{
- if (!enable_lacp(port, &s->active)) {
- return NULL;
- }
-
- s->name = port->name;
- memcpy(s->id, port->bridge->ea, ETH_ADDR_LEN);
- s->priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
- "0"));
- s->fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
- "fast");
-
- if (s->priority <= 0 || s->priority > UINT16_MAX) {
- /* Prefer bondable links if unspecified. */
- s->priority = UINT16_MAX - !list_is_short(&port->ifaces);
- }
- return s;
-}
-
static void
-iface_reconfigure_bond(struct iface *iface)
+iface_reconfigure_lacp(struct iface *iface)
{
struct lacp_slave_settings s;
int priority;
iface->cfg, "lacp-port-priority", "0"));
s.priority = (priority >= 0 && priority <= UINT16_MAX
? priority : UINT16_MAX);
- bond_slave_register(iface->port->bond, iface, iface->netdev, &s);
+ lacp_slave_register(iface->port->lacp, iface, &s);
+}
+
+static void
+port_reconfigure_lacp(struct port *port)
+{
+ static struct lacp_settings s;
+ struct iface *iface;
+
+ if (!enable_lacp(port, &s.active)) {
+ lacp_destroy(port->lacp);
+ port->lacp = NULL;
+ return;
+ }
+
+ s.name = port->name;
+ memcpy(s.id, port->bridge->ea, ETH_ADDR_LEN);
+ s.priority = atoi(get_port_other_config(port->cfg, "lacp-system-priority",
+ "0"));
+ s.fast = !strcmp(get_port_other_config(port->cfg, "lacp-time", "slow"),
+ "fast");
+
+ if (s.priority <= 0 || s.priority > UINT16_MAX) {
+ /* Prefer bondable links if unspecified. */
+ s.priority = UINT16_MAX - !list_is_short(&port->ifaces);
+ }
+
+ if (!port->lacp) {
+ port->lacp = lacp_create();
+ }
+
+ lacp_configure(port->lacp, &s);
+
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ iface_reconfigure_lacp(iface);
+ }
}
static void
port_reconfigure_bond(struct port *port)
{
- struct lacp_settings lacp_settings;
struct bond_settings s;
const char *detect_s;
struct iface *iface;
}
s.fake_iface = port->cfg->bond_fake_iface;
- s.lacp = port_reconfigure_bond_lacp(port, &lacp_settings);
+ s.lacp = port->lacp;
if (!port->bond) {
port->bond = bond_create(&s);
}
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface_reconfigure_bond(iface);
+ bond_slave_register(iface->port->bond, iface, iface->netdev);
}
}
bond_slave_unregister(port->bond, iface);
}
+ if (port->lacp) {
+ lacp_slave_unregister(port->lacp, iface);
+ }
+
shash_find_and_delete_assert(&br->iface_by_name, iface->name);
if (iface->dp_ifidx >= 0) {