This patch moves miimon logic from the bond module to netdev-linux.
This greatly simplifies the bonding code while adding minimal
complexity to netdev-linux. The bonding code is so high level, it
really has no business worrying about how precisely slave status is
determined.
tag_type stb_tag; /* Tag associated with this bond. */
/* Monitoring. */
- enum bond_detect_mode detect; /* Link status mode, one of BLSM_*. */
struct netdev_monitor *monitor; /* detect == BLSM_CARRIER only. */
- long long int miimon_interval; /* Miimon status refresh interval. */
- long long int miimon_next_update; /* Time of next miimon update. */
/* Legacy compatibility. */
long long int next_fake_iface_update; /* LLONG_MAX if disabled. */
static void bond_entry_reset(struct bond *);
static struct bond_slave *bond_slave_lookup(struct bond *, const void *slave_);
-static bool bond_is_link_up(struct bond *, struct netdev *);
static void bond_enable_slave(struct bond_slave *, bool enable,
struct tag_set *);
static void bond_link_status_update(struct bond_slave *, struct tag_set *);
NOT_REACHED();
}
-/* Attempts to parse 's' as the name of a bond link status detection mode. If
- * successful, stores the mode in '*detect' and returns true. Otherwise
- * returns false without modifying '*detect'. */
-bool
-bond_detect_mode_from_string(enum bond_detect_mode *detect, const char *s)
-{
- if (!strcmp(s, bond_detect_mode_to_string(BLSM_CARRIER))) {
- *detect = BLSM_CARRIER;
- } else if (!strcmp(s, bond_detect_mode_to_string(BLSM_MIIMON))) {
- *detect = BLSM_MIIMON;
- } else {
- return false;
- }
- return true;
-}
-
-/* Returns a string representing 'detect'. */
-const char *
-bond_detect_mode_to_string(enum bond_detect_mode detect)
-{
- switch (detect) {
- case BLSM_CARRIER:
- return "carrier";
- case BLSM_MIIMON:
- return "miimon";
- }
- NOT_REACHED();
-}
\f
/* Creates and returns a new bond whose configuration is initially taken from
* 's'.
hmap_init(&bond->slaves);
bond->no_slaves_tag = tag_create_random();
bond->stb_tag = tag_create_random();
- bond->miimon_next_update = LLONG_MAX;
bond->next_fake_iface_update = LLONG_MAX;
+ bond->monitor = netdev_monitor_create();
bond_reconfigure(bond, s);
hmap_insert(&all_bonds, &bond->hmap_node, hash_string(bond->name, 0));
}
- bond->detect = s->detect;
- bond->miimon_interval = s->miimon_interval;
bond->updelay = s->up_delay;
bond->downdelay = s->down_delay;
bond->rebalance_interval = s->rebalance_interval;
revalidate = true;
}
- if (bond->detect == BLSM_CARRIER) {
- struct bond_slave *slave;
-
- if (!bond->monitor) {
- bond->monitor = netdev_monitor_create();
- }
-
- HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
- netdev_monitor_add(bond->monitor, slave->netdev);
- }
- } else {
- netdev_monitor_destroy(bond->monitor);
- bond->monitor = NULL;
-
- if (bond->miimon_next_update == LLONG_MAX) {
- bond->miimon_next_update = time_msec() + bond->miimon_interval;
- }
- }
-
if (s->fake_iface) {
if (bond->next_fake_iface_update == LLONG_MAX) {
bond->next_fake_iface_update = time_msec();
struct netdev *netdev)
{
if (slave->netdev != netdev) {
- if (bond->monitor) {
- if (slave->netdev) {
- netdev_monitor_remove(bond->monitor, slave->netdev);
- }
- netdev_monitor_add(bond->monitor, netdev);
+ if (slave->netdev) {
+ netdev_monitor_remove(bond->monitor, slave->netdev);
}
+ netdev_monitor_add(bond->monitor, netdev);
slave->netdev = netdev;
}
}
slave->bond = bond;
slave->aux = slave_;
slave->delay_expires = LLONG_MAX;
- slave->up = bond_is_link_up(bond, netdev);
+ slave->up = netdev_get_carrier(netdev);
slave->name = xstrdup(netdev_get_name(netdev));
bond->bond_revalidate = true;
return;
}
- if (bond->monitor) {
- netdev_monitor_remove(bond->monitor, slave->netdev);
- }
-
+ netdev_monitor_remove(bond->monitor, slave->netdev);
bond_enable_slave(slave, false, NULL);
del_active = bond->active_slave == slave;
bond->lacp_negotiated = lacp_negotiated;
/* Update link status. */
- if (bond->detect == BLSM_CARRIER
- || time_msec() >= bond->miimon_next_update)
- {
- HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
- slave->up = bond_is_link_up(bond, slave->netdev);
- }
- bond->miimon_next_update = time_msec() + bond->miimon_interval;
+ HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
+ slave->up = netdev_get_carrier(slave->netdev);
}
if (bond->monitor) {
{
struct bond_slave *slave;
- if (bond->detect == BLSM_CARRIER) {
- netdev_monitor_poll_wait(bond->monitor);
- } else {
- poll_timer_wait_until(bond->miimon_next_update);
- }
+ netdev_monitor_poll_wait(bond->monitor);
HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
if (slave->delay_expires != LLONG_MAX) {
ds_put_format(&ds, "bond-hash-basis: %"PRIu32"\n", bond->basis);
- ds_put_format(&ds, "bond-detect-mode: %s\n",
- bond->monitor ? "carrier" : "miimon");
-
- if (!bond->monitor) {
- ds_put_format(&ds, "bond-miimon-interval: %lld\n",
- bond->miimon_interval);
- }
-
ds_put_format(&ds, "updelay: %d ms\n", bond->updelay);
ds_put_format(&ds, "downdelay: %d ms\n", bond->downdelay);
return NULL;
}
-static bool
-bond_is_link_up(struct bond *bond, struct netdev *netdev)
-{
- return (bond->detect == BLSM_CARRIER
- ? netdev_get_carrier(netdev)
- : netdev_get_miimon(netdev));
-}
-
static void
bond_enable_slave(struct bond_slave *slave, bool enable, struct tag_set *tags)
{
bool bond_mode_from_string(enum bond_mode *, const char *);
const char *bond_mode_to_string(enum bond_mode);
-/* How to detect link status. */
-enum bond_detect_mode {
- BLSM_CARRIER, /* Use carrier. */
- BLSM_MIIMON /* Poll MII status. */
-};
-
-bool bond_detect_mode_from_string(enum bond_detect_mode *, const char *);
-const char *bond_detect_mode_to_string(enum bond_detect_mode);
-
/* Configuration for a bond as a whole. */
struct bond_settings {
char *name; /* Bond's name, for log messages. */
int rebalance_interval; /* Milliseconds between rebalances. */
/* Link status detection. */
- enum bond_detect_mode detect; /* BLSM_CARRIER or BLSM_MIIMON. */
- int miimon_interval; /* Used only for BLSM_MIIMON. */
int up_delay; /* ms before enabling an up slave. */
int down_delay; /* ms before disabling a down slave. */
#include "socket-util.h"
#include "shash.h"
#include "sset.h"
+#include "timer.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(netdev_linux);
struct shash_node *shash_node;
unsigned int cache_valid;
+ bool miimon; /* Link status of last poll. */
+ long long int miimon_interval; /* Miimon Poll rate. Disabled if <= 0. */
+ struct timer miimon_timer;
+
/* The following are figured out "on demand" only. They are only valid
* when the corresponding VALID_* bit in 'cache_valid' is set. */
int ifindex;
static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
static int af_packet_sock(void);
+static void poll_notify(struct list *);
+static void netdev_linux_miimon_run(void);
+static void netdev_linux_miimon_wait(void);
static bool
is_netdev_linux_class(const struct netdev_class *netdev_class)
netdev_linux_run(void)
{
rtnetlink_link_notifier_run();
+ netdev_linux_miimon_run();
}
static void
netdev_linux_wait(void)
{
rtnetlink_link_notifier_wait();
+ netdev_linux_miimon_wait();
}
static void
char *fn = NULL;
int fd = -1;
+ if (netdev_dev->miimon_interval > 0) {
+ *carrier = netdev_dev->miimon;
+ return 0;
+ }
+
if (!(netdev_dev->cache_valid & VALID_CARRIER)) {
char line[8];
int retval;
}
static int
-netdev_linux_do_miimon(const struct netdev *netdev, int cmd,
- const char *cmd_name, struct mii_ioctl_data *data)
+netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name,
+ struct mii_ioctl_data *data)
{
struct ifreq ifr;
int error;
memset(&ifr, 0, sizeof ifr);
memcpy(&ifr.ifr_data, data, sizeof *data);
- error = netdev_linux_do_ioctl(netdev_get_name(netdev),
- &ifr, cmd, cmd_name);
+ error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name);
memcpy(data, &ifr.ifr_data, sizeof *data);
return error;
}
static int
-netdev_linux_get_miimon(const struct netdev *netdev, bool *miimon)
+netdev_linux_get_miimon(const char *name, bool *miimon)
{
- const char *name = netdev_get_name(netdev);
struct mii_ioctl_data data;
int error;
*miimon = false;
memset(&data, 0, sizeof data);
- error = netdev_linux_do_miimon(netdev, SIOCGMIIPHY, "SIOCGMIIPHY", &data);
+ error = netdev_linux_do_miimon(name, SIOCGMIIPHY, "SIOCGMIIPHY", &data);
if (!error) {
/* data.phy_id is filled out by previous SIOCGMIIPHY miimon call. */
data.reg_num = MII_BMSR;
- error = netdev_linux_do_miimon(netdev, SIOCGMIIREG, "SIOCGMIIREG",
+ error = netdev_linux_do_miimon(name, SIOCGMIIREG, "SIOCGMIIREG",
&data);
if (!error) {
return error;
}
+static int
+netdev_linux_set_miimon_interval(struct netdev *netdev_,
+ long long int interval)
+{
+ struct netdev_dev_linux *netdev_dev;
+
+ netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_));
+
+ interval = interval > 0 ? MAX(interval, 100) : 0;
+ if (netdev_dev->miimon_interval != interval) {
+ netdev_dev->miimon_interval = interval;
+ timer_set_expired(&netdev_dev->miimon_timer);
+ }
+
+ return 0;
+}
+
+static void
+netdev_linux_miimon_run(void)
+{
+ struct shash device_shash;
+ struct shash_node *node;
+
+ shash_init(&device_shash);
+ netdev_dev_get_devices(&netdev_linux_class, &device_shash);
+ SHASH_FOR_EACH (node, &device_shash) {
+ struct netdev_dev_linux *dev = node->data;
+ bool miimon;
+
+ if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) {
+ continue;
+ }
+
+ netdev_linux_get_miimon(dev->netdev_dev.name, &miimon);
+ if (miimon != dev->miimon) {
+ struct list *list;
+
+ dev->miimon = miimon;
+ list = shash_find_data(&netdev_linux_notifiers,
+ dev->netdev_dev.name);
+ if (list) {
+ poll_notify(list);
+ }
+ }
+
+ timer_set_duration(&dev->miimon_timer, dev->miimon_interval);
+ }
+
+ shash_destroy(&device_shash);
+}
+
+static void
+netdev_linux_miimon_wait(void)
+{
+ struct shash device_shash;
+ struct shash_node *node;
+
+ shash_init(&device_shash);
+ netdev_dev_get_devices(&netdev_linux_class, &device_shash);
+ SHASH_FOR_EACH (node, &device_shash) {
+ struct netdev_dev_linux *dev = node->data;
+
+ if (dev->miimon_interval > 0) {
+ timer_wait(&dev->miimon_timer);
+ }
+ }
+ shash_destroy(&device_shash);
+}
+
/* Check whether we can we use RTM_GETLINK to get network device statistics.
* In pre-2.6.19 kernels, this was only available if wireless extensions were
* enabled. */
netdev_linux_get_mtu, \
netdev_linux_get_ifindex, \
netdev_linux_get_carrier, \
- netdev_linux_get_miimon, \
+ netdev_linux_set_miimon_interval, \
netdev_linux_get_stats, \
SET_STATS, \
\
*/
int (*get_carrier)(const struct netdev *netdev, bool *carrier);
- /* Sets 'miimon' to true if 'netdev' is up according to its MII. If
- * 'netdev' does not support MII, may fall back to another method or return
- * EOPNOTSUPP.
+ /* Forces ->get_carrier() to poll 'netdev''s MII registers for link status
+ * instead of checking 'netdev''s carrier. 'netdev''s MII registers will
+ * be polled once ever 'interval' milliseconds. If 'netdev' does not
+ * support MII, another method may be used as a fallback. If 'interval' is
+ * less than or equal to zero, reverts ->get_carrier() to its normal
+ * behavior.
*
- * This function may be set to null if it would always return EOPNOTSUPP.
+ * Most network devices won't support this feature and will set this
+ * function pointer to NULL, which is equivalent to returning EOPNOTSUPP.
*/
- int (*get_miimon)(const struct netdev *netdev, bool *miimon);
+ int (*set_miimon_interval)(struct netdev *netdev, long long int interval);
/* Retrieves current device stats for 'netdev' into 'stats'.
*
return carrier;
}
-/* Returns true if 'netdev' is up according to its MII. */
-bool
-netdev_get_miimon(const struct netdev *netdev)
+/* Attempts to force netdev_get_carrier() to poll 'netdev''s MII registers for
+ * link status instead of checking 'netdev''s carrier. 'netdev''s MII
+ * registers will be polled once ever 'interval' milliseconds. If 'netdev'
+ * does not support MII, another method may be used as a fallback. If
+ * 'interval' is less than or equal to zero, reverts netdev_get_carrier() to
+ * its normal behavior.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+netdev_set_miimon_interval(struct netdev *netdev, long long int interval)
{
- int error;
- enum netdev_flags flags;
- bool miimon;
-
- netdev_get_flags(netdev, &flags);
- if (!(flags & NETDEV_UP)) {
- return false;
- }
-
- if (!netdev_get_dev(netdev)->netdev_class->get_miimon) {
- return true;
- }
-
- error = netdev_get_dev(netdev)->netdev_class->get_miimon(netdev, &miimon);
-
- if (error) {
- VLOG_DBG("%s: failed to get network device MII status, assuming "
- "down: %s", netdev_get_name(netdev), strerror(error));
- miimon = false;
- }
-
- return miimon;
+ struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
+ return (netdev_dev->netdev_class->set_miimon_interval
+ ? netdev_dev->netdev_class->set_miimon_interval(netdev, interval)
+ : EOPNOTSUPP);
}
/* Retrieves current device stats for 'netdev'. */
/* PHY interface. */
bool netdev_get_carrier(const struct netdev *);
-bool netdev_get_miimon(const struct netdev *);
+int netdev_set_miimon_interval(struct netdev *, long long int interval);
int netdev_get_features(const struct netdev *,
uint32_t *current, uint32_t *advertised,
uint32_t *supported, uint32_t *peer);
} else {
s.bond = NULL;
s.bond_stable_ids = NULL;
+
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ netdev_set_miimon_interval(iface->netdev, 0);
+ }
}
/* Register. */
{
const char *detect_s;
struct iface *iface;
+ int miimon_interval;
size_t i;
s->name = port->name;
bond_mode_to_string(s->balance));
}
- s->detect = BLSM_CARRIER;
- detect_s = get_port_other_config(port->cfg, "bond-detect-mode", NULL);
- if (detect_s && !bond_detect_mode_from_string(&s->detect, detect_s)) {
- VLOG_WARN("port %s: unsupported bond-detect-mode %s, "
- "defaulting to %s",
- port->name, detect_s, bond_detect_mode_to_string(s->detect));
+ miimon_interval = atoi(get_port_other_config(port->cfg,
+ "bond-miimon-interval", "0"));
+ if (miimon_interval <= 0) {
+ miimon_interval = 200;
}
- s->miimon_interval = atoi(
- get_port_other_config(port->cfg, "bond-miimon-interval", "200"));
- if (s->miimon_interval < 100) {
- s->miimon_interval = 100;
+ detect_s = get_port_other_config(port->cfg, "bond-detect-mode", "carrier");
+ if (!strcmp(detect_s, "carrier")) {
+ miimon_interval = 0;
+ } else if (strcmp(detect_s, "miimon")) {
+ VLOG_WARN("port %s: unsupported bond-detect-mode %s, "
+ "defaulting to carrier", port->name, detect_s);
+ miimon_interval = 0;
}
s->up_delay = MAX(0, port->cfg->bond_updelay);
stable_id = iface->ofp_port;
}
bond_stable_ids[i++] = stable_id;
+
+ netdev_set_miimon_interval(iface->netdev, miimon_interval);
}
}
\f