X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=vswitchd%2Fbridge.c;fp=vswitchd%2Fbridge.c;h=1145e98db5efcae89d17e077141939acdbc6fb56;hb=7d78f21c057ff50a823220d809ac38c3d907243c;hp=f46d002ebd2df84f2cdbe8c4b79176bb6280220b;hpb=8d25251929c8f325bed0fff24192d5a87034b32e;p=sliver-openvswitch.git diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index f46d002eb..1145e98db 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -77,6 +77,7 @@ struct iface { char *name; /* Host network device name. */ struct netdev *netdev; /* Network device. */ ofp_port_t ofp_port; /* OpenFlow port number. */ + uint64_t change_seq; /* These members are valid only within bridge_reconfigure(). */ const char *type; /* Usually same as cfg->type. */ @@ -160,10 +161,29 @@ static unsigned int idl_seqno; /* Track changes to port connectivity. */ static uint64_t connectivity_seqno = LLONG_MIN; +/* Status update to database. + * + * Some information in the database must be kept as up-to-date as possible to + * allow controllers to respond rapidly to network outages. Those status are + * updated via the 'status_txn'. + * + * We use the global connectivity sequence number to detect the status change. + * Also, to prevent the status update from sending too much to the database, + * we check the return status of each update transaction and do not start new + * update if the previous transaction status is 'TXN_INCOMPLETE'. + * + * 'statux_txn' is NULL if there is no ongoing status update. + */ +static struct ovsdb_idl_txn *status_txn; + +/* When the status update transaction returns 'TXN_INCOMPLETE', should register a + * timeout in 'STATUS_CHECK_AGAIN_MSEC' to check again. */ +#define STATUS_CHECK_AGAIN_MSEC 100 + /* Each time this timer expires, the bridge fetches interface and mirror * statistics and pushes them into the database. */ -#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */ -static long long int iface_stats_timer = LLONG_MIN; +static int stats_timer_interval; +static long long int stats_timer = LLONG_MIN; /* Set to true to allow experimental use of OpenFlow 1.4. * This is false initially because OpenFlow 1.4 is not yet safe to use: it can @@ -252,6 +272,7 @@ static bool iface_is_internal(const struct ovsrec_interface *iface, static const char *iface_get_type(const struct ovsrec_interface *, const struct ovsrec_bridge *); static void iface_destroy(struct iface *); +static void iface_destroy__(struct iface *); static struct iface *iface_lookup(const struct bridge *, const char *name); static struct iface *iface_find(const char *name); static struct iface *iface_from_ofp_port(const struct bridge *, @@ -263,7 +284,8 @@ static void iface_configure_qos(struct iface *, const struct ovsrec_qos *); static void iface_configure_cfm(struct iface *); static void iface_refresh_cfm_stats(struct iface *); static void iface_refresh_stats(struct iface *); -static void iface_refresh_status(struct iface *); +static void iface_refresh_netdev_status(struct iface *); +static void iface_refresh_ofproto_status(struct iface *); static bool iface_is_synthetic(const struct iface *); static ofp_port_t iface_get_requested_ofp_port( const struct ovsrec_interface *); @@ -604,13 +626,6 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) bridge_configure_stp(br); bridge_configure_tables(br); bridge_configure_dp_desc(br); - - if (smap_get(&br->cfg->other_config, "flow-eviction-threshold")) { - /* XXX: Remove this warning message eventually. */ - VLOG_WARN_ONCE("As of June 2013, flow-eviction-threshold has been" - " moved to the Open_vSwitch table. Ignoring its" - " setting in the bridge table."); - } } free(managers); @@ -667,6 +682,9 @@ bridge_delete_or_reconfigure_ports(struct bridge *br) struct ofproto_port ofproto_port; struct ofproto_port_dump dump; + struct sset ofproto_ports; + struct port *port, *port_next; + /* List of "ofp_port"s to delete. We make a list instead of deleting them * right away because ofproto implementations aren't necessarily able to * iterate through a changing list of ports in an entirely robust way. */ @@ -676,11 +694,21 @@ bridge_delete_or_reconfigure_ports(struct bridge *br) del = NULL; n = allocated = 0; + sset_init(&ofproto_ports); + /* Main task: Iterate over the ports in 'br->ofproto' and remove the ports + * that are not configured in the database. (This commonly happens when + * ports have been deleted, e.g. with "ovs-vsctl del-port".) + * + * Side tasks: Reconfigure the ports that are still in 'br'. Delete ports + * that have the wrong OpenFlow port number (and arrange to add them back + * with the correct OpenFlow port number). */ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) { ofp_port_t requested_ofp_port; struct iface *iface; + sset_add(&ofproto_ports, ofproto_port.name); + iface = iface_lookup(br, ofproto_port.name); if (!iface) { /* No such iface is configured, so we should delete this @@ -746,11 +774,37 @@ bridge_delete_or_reconfigure_ports(struct bridge *br) iface_destroy(iface); del = add_ofp_port(ofproto_port.ofp_port, del, &n, &allocated); } - for (i = 0; i < n; i++) { ofproto_port_del(br->ofproto, del[i]); } free(del); + + /* Iterate over this module's idea of interfaces in 'br'. Remove any ports + * that we didn't see when we iterated through the datapath, i.e. ports + * that disappeared underneath use. This is an unusual situation, but it + * can happen in some cases: + * + * - An admin runs a command like "ovs-dpctl del-port" (which is a bad + * idea but could happen). + * + * - The port represented a device that disappeared, e.g. a tuntap + * device destroyed via "tunctl -d", a physical Ethernet device + * whose module was just unloaded via "rmmod", or a virtual NIC for a + * VM whose VM was just terminated. */ + HMAP_FOR_EACH_SAFE (port, port_next, hmap_node, &br->ports) { + struct iface *iface, *iface_next; + + LIST_FOR_EACH_SAFE (iface, iface_next, port_elem, &port->ifaces) { + if (!sset_contains(&ofproto_ports, iface->name)) { + iface_destroy__(iface); + } + } + + if (list_is_empty(&port->ifaces)) { + port_destroy(port); + } + } + sset_destroy(&ofproto_ports); } static void @@ -1506,7 +1560,7 @@ iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg, /* Populate initial status in database. */ iface_refresh_stats(iface); - iface_refresh_status(iface); + iface_refresh_netdev_status(iface); /* Add bond fake iface if necessary. */ if (port_is_bond_fake_iface(port)) { @@ -1764,22 +1818,27 @@ dpid_from_hash(const void *data, size_t n) } static void -iface_refresh_status(struct iface *iface) +iface_refresh_netdev_status(struct iface *iface) { struct smap smap; enum netdev_features current; - int64_t bps; - int mtu; - int64_t mtu_64; + enum netdev_flags flags; + const char *link_state; uint8_t mac[ETH_ADDR_LEN]; - int64_t ifindex64; - int error; + int64_t bps, mtu_64, ifindex64, link_resets; + int mtu, error; if (iface_is_synthetic(iface)) { return; } + if (iface->change_seq == netdev_get_change_seq(iface->netdev)) { + return; + } + + iface->change_seq = netdev_get_change_seq(iface->netdev); + smap_init(&smap); if (!netdev_get_status(iface->netdev, &smap)) { @@ -1790,6 +1849,21 @@ iface_refresh_status(struct iface *iface) smap_destroy(&smap); + error = netdev_get_flags(iface->netdev, &flags); + if (!error) { + const char *state = flags & NETDEV_UP ? "up" : "down"; + + ovsrec_interface_set_admin_state(iface->cfg, state); + } else { + ovsrec_interface_set_admin_state(iface->cfg, NULL); + } + + link_state = netdev_get_carrier(iface->netdev) ? "up" : "down"; + ovsrec_interface_set_link_state(iface->cfg, link_state); + + link_resets = netdev_get_carrier_resets(iface->netdev); + ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1); + error = netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL); bps = !error ? netdev_features_to_bps(current, 0) : 0; if (bps) { @@ -1829,6 +1903,36 @@ iface_refresh_status(struct iface *iface) ovsrec_interface_set_ifindex(iface->cfg, &ifindex64, 1); } +static void +iface_refresh_ofproto_status(struct iface *iface) +{ + struct smap smap; + int current, error; + + if (iface_is_synthetic(iface)) { + return; + } + + current = ofproto_port_is_lacp_current(iface->port->bridge->ofproto, + iface->ofp_port); + if (current >= 0) { + bool bl = current; + ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1); + } else { + ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0); + } + + iface_refresh_cfm_stats(iface); + + smap_init(&smap); + error = ofproto_port_get_bfd_status(iface->port->bridge->ofproto, + iface->ofp_port, &smap); + if (error >= 0) { + ovsrec_interface_set_bfd_status(iface->cfg, &smap); + } + smap_destroy(&smap); +} + /* Writes 'iface''s CFM statistics to the database. 'iface' must not be * synthetic. */ static void @@ -1836,9 +1940,13 @@ iface_refresh_cfm_stats(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; struct ofproto_cfm_status status; + int error; - if (!ofproto_port_get_cfm_status(iface->port->bridge->ofproto, - iface->ofp_port, &status)) { + error = ofproto_port_get_cfm_status(iface->port->bridge->ofproto, + iface->ofp_port, &status); + if (error < 0) { + /* Do nothing if there is no status change since last update. */ + } else if (error > 0) { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); @@ -2138,142 +2246,6 @@ refresh_controller_status(void) ofproto_free_ofproto_controller_info(&info); } -/* "Instant" stats. - * - * Some information in the database must be kept as up-to-date as possible to - * allow controllers to respond rapidly to network outages. We call these - * statistics "instant" stats. - * - * We wish to update these statistics every INSTANT_INTERVAL_MSEC milliseconds, - * assuming that they've changed. The only means we have to determine whether - * they have changed are: - * - * - Try to commit changes to the database. If nothing changed, then - * ovsdb_idl_txn_commit() returns TXN_UNCHANGED, otherwise some other - * value. - * - * - instant_stats_run() is called late in the run loop, after anything that - * might change any of the instant stats. - * - * We use these two facts together to avoid waking the process up every - * INSTANT_INTERVAL_MSEC whether there is any change or not. - */ - -/* Minimum interval between writing updates to the instant stats to the - * database. */ -#define INSTANT_INTERVAL_MSEC 100 - -/* Current instant stats database transaction, NULL if there is no ongoing - * transaction. */ -static struct ovsdb_idl_txn *instant_txn; - -/* Next time (in msec on monotonic clock) at which we will update the instant - * stats. */ -static long long int instant_next_txn = LLONG_MIN; - -/* True if the run loop has run since we last saw that the instant stats were - * unchanged, that is, this is true if we need to wake up at 'instant_next_txn' - * to refresh the instant stats. */ -static bool instant_stats_could_have_changed; - -static void -instant_stats_run(void) -{ - enum ovsdb_idl_txn_status status; - - instant_stats_could_have_changed = true; - - if (!instant_txn) { - struct bridge *br; - uint64_t seq; - - if (time_msec() < instant_next_txn) { - return; - } - instant_next_txn = time_msec() + INSTANT_INTERVAL_MSEC; - - seq = seq_read(connectivity_seq_get()); - if (seq == connectivity_seqno) { - return; - } - connectivity_seqno = seq; - - instant_txn = ovsdb_idl_txn_create(idl); - HMAP_FOR_EACH (br, node, &all_bridges) { - struct iface *iface; - struct port *port; - - br_refresh_stp_status(br); - - HMAP_FOR_EACH (port, hmap_node, &br->ports) { - port_refresh_stp_status(port); - } - - HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) { - enum netdev_flags flags; - struct smap smap; - const char *link_state; - int64_t link_resets; - int current, error; - - if (iface_is_synthetic(iface)) { - continue; - } - - current = ofproto_port_is_lacp_current(br->ofproto, - iface->ofp_port); - if (current >= 0) { - bool bl = current; - ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1); - } else { - ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0); - } - - error = netdev_get_flags(iface->netdev, &flags); - if (!error) { - const char *state = flags & NETDEV_UP ? "up" : "down"; - ovsrec_interface_set_admin_state(iface->cfg, state); - } else { - ovsrec_interface_set_admin_state(iface->cfg, NULL); - } - - link_state = netdev_get_carrier(iface->netdev) ? "up" : "down"; - ovsrec_interface_set_link_state(iface->cfg, link_state); - - link_resets = netdev_get_carrier_resets(iface->netdev); - ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1); - - iface_refresh_cfm_stats(iface); - - smap_init(&smap); - ofproto_port_get_bfd_status(br->ofproto, iface->ofp_port, - &smap); - ovsrec_interface_set_bfd_status(iface->cfg, &smap); - smap_destroy(&smap); - } - } - } - - status = ovsdb_idl_txn_commit(instant_txn); - if (status != TXN_INCOMPLETE) { - ovsdb_idl_txn_destroy(instant_txn); - instant_txn = NULL; - } - if (status == TXN_UNCHANGED) { - instant_stats_could_have_changed = false; - } -} - -static void -instant_stats_wait(void) -{ - if (instant_txn) { - ovsdb_idl_txn_wait(instant_txn); - } else if (instant_stats_could_have_changed) { - poll_timer_wait_until(instant_next_txn); - } -} - static void bridge_run__(void) { @@ -2303,6 +2275,7 @@ bridge_run(void) bool vlan_splinters_changed; struct bridge *br; + int stats_interval; ovsrec_open_vswitch_init(&null_cfg); @@ -2408,8 +2381,17 @@ bridge_run(void) } } + /* Statistics update interval should always be greater than or equal to + * 5000 ms. */ + stats_interval = MAX(smap_get_int(&cfg->other_config, + "stats-update-interval", 5000), 5000); + if (stats_timer_interval != stats_interval) { + stats_timer_interval = stats_interval; + stats_timer = LLONG_MIN; + } + /* Refresh interface and mirror stats if necessary. */ - if (time_msec() >= iface_stats_timer) { + if (time_msec() >= stats_timer) { if (cfg) { struct ovsdb_idl_txn *txn; @@ -2423,7 +2405,6 @@ bridge_run(void) LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_refresh_stats(iface); - iface_refresh_status(iface); } port_refresh_stp_stats(port); @@ -2439,11 +2420,47 @@ bridge_run(void) ovsdb_idl_txn_destroy(txn); /* XXX */ } - iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL; + stats_timer = time_msec() + stats_timer_interval; + } + + if (!status_txn) { + uint64_t seq; + + /* Check the need to update status. */ + seq = seq_read(connectivity_seq_get()); + if (seq != connectivity_seqno) { + connectivity_seqno = seq; + status_txn = ovsdb_idl_txn_create(idl); + HMAP_FOR_EACH (br, node, &all_bridges) { + struct port *port; + + br_refresh_stp_status(br); + HMAP_FOR_EACH (port, hmap_node, &br->ports) { + struct iface *iface; + + port_refresh_stp_status(port); + LIST_FOR_EACH (iface, port_elem, &port->ifaces) { + iface_refresh_netdev_status(iface); + iface_refresh_ofproto_status(iface); + } + } + } + } + } + + if (status_txn) { + enum ovsdb_idl_txn_status status; + + status = ovsdb_idl_txn_commit(status_txn); + /* Do not destroy "status_txn" if the transaction is + * "TXN_INCOMPLETE". */ + if (status != TXN_INCOMPLETE) { + ovsdb_idl_txn_destroy(status_txn); + status_txn = NULL; + } } run_system_stats(); - instant_stats_run(); } void @@ -2470,11 +2487,21 @@ bridge_wait(void) HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_wait(br->ofproto); } - poll_timer_wait_until(iface_stats_timer); + + poll_timer_wait_until(stats_timer); + } + + /* If the status database transaction is 'TXN_INCOMPLETE' in this run, + * register a timeout in 'STATUS_CHECK_AGAIN_MSEC'. Else, wait on the + * global connectivity sequence number. Note, this also helps batch + * multiple status changes into one transaction. */ + if (status_txn) { + poll_timer_wait_until(time_msec() + STATUS_CHECK_AGAIN_MSEC); + } else { + seq_wait(connectivity_seq_get(), connectivity_seqno); } system_stats_wait(); - instant_stats_wait(); } /* Adds some memory usage statistics for bridges into 'usage', for use with @@ -3145,8 +3172,6 @@ bridge_configure_dp_desc(struct bridge *br) /* Port functions. */ -static void iface_destroy__(struct iface *); - static struct port * port_create(struct bridge *br, const struct ovsrec_port *cfg) {