#include "ofpbuf.h"
#include "ofproto/netflow.h"
#include "ofproto/ofproto.h"
+#include "ovsdb-data.h"
#include "packets.h"
#include "poll-loop.h"
#include "port-array.h"
#include "vconn.h"
#include "vswitchd/vswitch-idl.h"
#include "xenserver.h"
+#include "vlog.h"
#include "xtoxll.h"
#include "sflow_api.h"
-#define THIS_MODULE VLM_bridge
-#include "vlog.h"
+VLOG_DEFINE_THIS_MODULE(bridge)
struct dst {
uint16_t vlan;
long long delay_expires; /* Time after which 'enabled' may change. */
/* These members are valid only after bridge_reconfigure() causes them to
- * be initialized.*/
+ * be initialized. */
int dp_ifidx; /* Index within kernel datapath. */
struct netdev *netdev; /* Network device. */
bool enabled; /* May be chosen for flows? */
-
- /* This member is only valid *during* bridge_reconfigure(). */
const struct ovsrec_interface *cfg;
};
struct bridge *bridge;
size_t idx;
char *name;
+ struct uuid uuid; /* UUID of this "mirror" record in database. */
/* Selection criteria. */
struct shash src_ports; /* Name is port name; data is always NULL. */
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
+ const struct ovsrec_port *cfg;
char *name;
/* An ordinary bridge port has 1 interface.
mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */
mirror_mask_t dst_mirrors; /* Mirrors triggered when packet sent. */
bool is_mirror_output_port; /* Does port mirroring send frames here? */
-
- /* This member is only valid *during* bridge_reconfigure(). */
- const struct ovsrec_port *cfg;
};
#define DP_MAX_PORTS 255
char *name; /* User-specified arbitrary name. */
struct mac_learning *ml; /* MAC learning table. */
uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
+ const struct ovsrec_bridge *cfg;
/* OpenFlow switch processing. */
struct ofproto *ofproto; /* OpenFlow switch. */
/* Port mirroring. */
struct mirror *mirrors[MAX_MIRRORS];
-
- /* This member is only valid *during* bridge_reconfigure(). */
- const struct ovsrec_bridge *cfg;
};
/* List of all bridges. */
/* OVSDB IDL used to obtain configuration. */
static struct ovsdb_idl *idl;
+/* Each time this timer expires, the bridge fetches statistics for every
+ * interface and pushes them into the database. */
+#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+static long long int iface_stats_timer = LLONG_MIN;
+
static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg);
static void bridge_destroy(struct bridge *);
static struct bridge *bridge_lookup(const char *name);
static unixctl_cb_func bridge_unixctl_dump_flows;
+static unixctl_cb_func bridge_unixctl_reconnect;
static int bridge_run_one(struct bridge *);
static size_t bridge_get_controllers(const struct ovsrec_open_vswitch *ovs_cfg,
const struct bridge *br,
static void port_update_vlan_compat(struct port *);
static void port_update_bonding(struct port *);
-static struct mirror *mirror_create(struct bridge *, const char *name);
+static void mirror_create(struct bridge *, struct ovsrec_mirror *);
static void mirror_destroy(struct mirror *);
static void mirror_reconfigure(struct bridge *);
static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *);
unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL);
unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows,
NULL);
+ unixctl_command_register("bridge/reconnect", bridge_unixctl_reconnect,
+ NULL);
bond_init();
}
}
already_configured_once = true;
+ iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
+
/* Get all the configured bridges' names from 'cfg' into 'bridge_names'. */
svec_init(&bridge_names);
for (i = 0; i < cfg->n_bridges; i++) {
shash_init(&cur_ifaces);
for (i = 0; i < n_dpif_ports; i++) {
const char *name = dpif_ports[i].devname;
- if (!shash_find(&cur_ifaces, name)) {
- shash_add(&cur_ifaces, name, NULL);
- }
+ shash_add_once(&cur_ifaces, name, NULL);
}
free(dpif_ports);
}
static const char *
-get_ovsrec_key_value(const char *key, char **keys, char **values, size_t n)
+get_ovsrec_key_value(const struct ovsdb_idl_row *row,
+ const struct ovsdb_idl_column *column,
+ const char *key)
{
- size_t i;
-
- for (i = 0; i < n; i++) {
- if (!strcmp(keys[i], key)) {
- return values[i];
- }
- }
- return NULL;
+ const struct ovsdb_datum *datum;
+ union ovsdb_atom atom;
+ unsigned int idx;
+
+ datum = ovsdb_idl_get(row, column, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
+ atom.string = (char *) key;
+ idx = ovsdb_datum_find_key(datum, &atom, OVSDB_TYPE_STRING);
+ return idx == UINT_MAX ? NULL : datum->values[idx].string;
}
static const char *
bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key)
{
- return get_ovsrec_key_value(key,
- br_cfg->key_other_config,
- br_cfg->value_other_config,
- br_cfg->n_other_config);
+ return get_ovsrec_key_value(&br_cfg->header_,
+ &ovsrec_bridge_col_other_config, key);
}
static void
return eth_addr_to_uint64(hash);
}
+static void
+iface_refresh_stats(struct iface *iface)
+{
+ struct iface_stat {
+ char *name;
+ int offset;
+ };
+ static const struct iface_stat iface_stats[] = {
+ { "rx_packets", offsetof(struct netdev_stats, rx_packets) },
+ { "tx_packets", offsetof(struct netdev_stats, tx_packets) },
+ { "rx_bytes", offsetof(struct netdev_stats, rx_bytes) },
+ { "tx_bytes", offsetof(struct netdev_stats, tx_bytes) },
+ { "rx_dropped", offsetof(struct netdev_stats, rx_dropped) },
+ { "tx_dropped", offsetof(struct netdev_stats, tx_dropped) },
+ { "rx_errors", offsetof(struct netdev_stats, rx_errors) },
+ { "tx_errors", offsetof(struct netdev_stats, tx_errors) },
+ { "rx_frame_err", offsetof(struct netdev_stats, rx_frame_errors) },
+ { "rx_over_err", offsetof(struct netdev_stats, rx_over_errors) },
+ { "rx_crc_err", offsetof(struct netdev_stats, rx_crc_errors) },
+ { "collisions", offsetof(struct netdev_stats, collisions) },
+ };
+ enum { N_STATS = ARRAY_SIZE(iface_stats) };
+ const struct iface_stat *s;
+
+ char *keys[N_STATS];
+ int64_t values[N_STATS];
+ int n;
+
+ struct netdev_stats stats;
+
+ /* Intentionally ignore return value, since errors will set 'stats' to
+ * all-1s, and we will deal with that correctly below. */
+ netdev_get_stats(iface->netdev, &stats);
+
+ n = 0;
+ for (s = iface_stats; s < &iface_stats[N_STATS]; s++) {
+ uint64_t value = *(uint64_t *) (((char *) &stats) + s->offset);
+ if (value != UINT64_MAX) {
+ keys[n] = s->name;
+ values[n] = value;
+ n++;
+ }
+ }
+
+ ovsrec_interface_set_statistics(iface->cfg, keys, values, n);
+}
+
void
bridge_run(void)
{
ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg);
ovsdb_idl_txn_commit(txn);
ovsdb_idl_txn_destroy(txn); /* XXX */
+ } else {
+ /* We still need to reconfigure to avoid dangling pointers to
+ * now-destroyed ovsrec structures inside bridge data. */
+ static const struct ovsrec_open_vswitch null_cfg;
+
+ bridge_reconfigure(&null_cfg);
}
}
+
+ /* Refresh interface stats if necessary. */
+ if (time_msec() >= iface_stats_timer) {
+ struct ovsdb_idl_txn *txn;
+
+ txn = ovsdb_idl_txn_create(idl);
+ LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+ size_t i;
+
+ for (i = 0; i < br->n_ports; i++) {
+ struct port *port = br->ports[i];
+ size_t j;
+
+ for (j = 0; j < port->n_ifaces; j++) {
+ struct iface *iface = port->ifaces[j];
+ iface_refresh_stats(iface);
+ }
+ }
+ }
+ ovsdb_idl_txn_commit(txn);
+ ovsdb_idl_txn_destroy(txn); /* XXX */
+
+ iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
+ }
}
void
bond_wait(br);
}
ovsdb_idl_wait(idl);
+ poll_timer_wait_until(iface_stats_timer);
}
/* Forces 'br' to revalidate all of its flows. This is appropriate when 'br''s
ds_destroy(&results);
}
+/* "bridge/reconnect [BRIDGE]": makes BRIDGE drop all of its controller
+ * connections and reconnect. If BRIDGE is not specified, then all bridges
+ * drop their controller connections and reconnect. */
+static void
+bridge_unixctl_reconnect(struct unixctl_conn *conn,
+ const char *args, void *aux OVS_UNUSED)
+{
+ struct bridge *br;
+ if (args[0] != '\0') {
+ br = bridge_lookup(args);
+ if (!br) {
+ unixctl_command_reply(conn, 501, "Unknown bridge");
+ return;
+ }
+ ofproto_reconnect_controllers(br->ofproto);
+ } else {
+ LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+ ofproto_reconnect_controllers(br->ofproto);
+ }
+ }
+ unixctl_command_reply(conn, 200, NULL);
+}
+
static int
bridge_run_one(struct bridge *br)
{
get_port_other_config(const struct ovsrec_port *port, const char *key,
const char *default_value)
{
- const char *value = get_ovsrec_key_value(key,
- port->key_other_config,
- port->value_other_config,
- port->n_other_config);
+ const char *value;
+
+ value = get_ovsrec_key_value(&port->header_, &ovsrec_port_col_other_config,
+ key);
return value ? value : default_value;
}
struct iface_delete_queues_cbdata {
struct netdev *netdev;
- const int64_t *queue_ids;
- size_t n_queue_ids;
+ const struct ovsdb_datum *queues;
};
static bool
-queue_ids_include(const int64_t *ids, size_t n, int64_t target)
+queue_ids_include(const struct ovsdb_datum *queues, int64_t target)
{
- size_t low = 0;
- size_t high = n;
+ union ovsdb_atom atom;
- while (low < high) {
- size_t mid = low + (high - low) / 2;
- if (target > ids[mid]) {
- high = mid;
- } else if (target < ids[mid]) {
- low = mid + 1;
- } else {
- return true;
- }
- }
- return false;
+ atom.integer = target;
+ return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER) != UINT_MAX;
}
static void
{
struct iface_delete_queues_cbdata *cbdata = cbdata_;
- if (!queue_ids_include(cbdata->queue_ids, cbdata->n_queue_ids, queue_id)) {
+ if (!queue_ids_include(cbdata->queues, queue_id)) {
netdev_delete_queue(cbdata->netdev, queue_id);
}
}
/* Deconfigure queues that were deleted. */
cbdata.netdev = iface->netdev;
- cbdata.queue_ids = qos->key_queues;
- cbdata.n_queue_ids = qos->n_queues;
+ cbdata.queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER,
+ OVSDB_TYPE_UUID);
netdev_dump_queues(iface->netdev, iface_delete_queues, &cbdata);
/* Configure queues for 'iface'. */
\f
/* Port mirroring. */
+static struct mirror *
+mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid)
+{
+ int i;
+
+ for (i = 0; i < MAX_MIRRORS; i++) {
+ struct mirror *m = br->mirrors[i];
+ if (m && uuid_equals(uuid, &m->uuid)) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
static void
mirror_reconfigure(struct bridge *br)
{
- struct shash old_mirrors, new_mirrors;
- struct shash_node *node;
unsigned long *rspan_vlans;
int i;
- /* Collect old mirrors. */
- shash_init(&old_mirrors);
+ /* Get rid of deleted mirrors. */
for (i = 0; i < MAX_MIRRORS; i++) {
- if (br->mirrors[i]) {
- shash_add(&old_mirrors, br->mirrors[i]->name, br->mirrors[i]);
+ struct mirror *m = br->mirrors[i];
+ if (m) {
+ const struct ovsdb_datum *mc;
+ union ovsdb_atom atom;
+
+ mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
+ atom.uuid = br->mirrors[i]->uuid;
+ if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
+ mirror_destroy(m);
+ }
}
}
- /* Collect new mirrors. */
- shash_init(&new_mirrors);
+ /* Add new mirrors and reconfigure existing ones. */
for (i = 0; i < br->cfg->n_mirrors; i++) {
struct ovsrec_mirror *cfg = br->cfg->mirrors[i];
- if (!shash_add_once(&new_mirrors, cfg->name, cfg)) {
- VLOG_WARN("bridge %s: %s specified twice as mirror",
- br->name, cfg->name);
- }
- }
-
- /* Get rid of deleted mirrors and add new mirrors. */
- SHASH_FOR_EACH (node, &old_mirrors) {
- if (!shash_find(&new_mirrors, node->name)) {
- mirror_destroy(node->data);
- }
- }
- SHASH_FOR_EACH (node, &new_mirrors) {
- struct mirror *mirror = shash_find_data(&old_mirrors, node->name);
- if (!mirror) {
- mirror = mirror_create(br, node->name);
- if (!mirror) {
- break;
- }
+ struct mirror *m = mirror_find_by_uuid(br, &cfg->header_.uuid);
+ if (m) {
+ mirror_reconfigure_one(m, cfg);
+ } else {
+ mirror_create(br, cfg);
}
- mirror_reconfigure_one(mirror, node->data);
}
- shash_destroy(&old_mirrors);
- shash_destroy(&new_mirrors);
/* Update port reserved status. */
for (i = 0; i < br->n_ports; i++) {
}
}
-static struct mirror *
-mirror_create(struct bridge *br, const char *name)
+static void
+mirror_create(struct bridge *br, struct ovsrec_mirror *cfg)
{
struct mirror *m;
size_t i;
for (i = 0; ; i++) {
if (i >= MAX_MIRRORS) {
VLOG_WARN("bridge %s: maximum of %d port mirrors reached, "
- "cannot create %s", br->name, MAX_MIRRORS, name);
- return NULL;
+ "cannot create %s", br->name, MAX_MIRRORS, cfg->name);
+ return;
}
if (!br->mirrors[i]) {
break;
}
}
- VLOG_INFO("created port mirror %s on bridge %s", name, br->name);
+ VLOG_INFO("created port mirror %s on bridge %s", cfg->name, br->name);
bridge_flush(br);
br->mirrors[i] = m = xzalloc(sizeof *m);
m->bridge = br;
m->idx = i;
- m->name = xstrdup(name);
+ m->name = xstrdup(cfg->name);
shash_init(&m->src_ports);
shash_init(&m->dst_ports);
m->vlans = NULL;
m->out_vlan = -1;
m->out_port = NULL;
- return m;
+ mirror_reconfigure_one(m, cfg);
}
static void
free(m->vlans);
m->bridge->mirrors[m->idx] = NULL;
+ free(m->name);
free(m);
bridge_flush(br);
int *vlans;
size_t i;
+ /* Set name. */
+ if (strcmp(cfg->name, m->name)) {
+ free(m->name);
+ m->name = xstrdup(cfg->name);
+ }
+
/* Get output port. */
if (cfg->output_port) {
out_port = port_lookup(m->bridge, cfg->output_port->name);