From d534627818f126982e1d1aadf8919762de14d409 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 18 Mar 2011 15:42:29 -0700 Subject: [PATCH] bridge: Avoid flushing entire MAC learning table for common operations. Adding and removing ports is fairly common in a virtual environment, because it happens whenever a VM boots or shuts down. It is best to avoid flushing the whole MAC learning table when that happens, because that means that, briefly, every packet will get flooded, wasting CPU cycles and network bandwidth. This commit breaks flushing the MAC table out of bridge_flush(). Instead, each caller is now responsible for flushing the MAC table if it is necessary. In a few cases, no flushing was necessary, so those callers were not modified. In the case of removing a port or modifying its VLAN assignments, it is necessary to expire all of the MAC learning entries associated with that port, so this commit does that. Finally, some operations do require a MAC learning flush but they are rare enough that in my opinion it's not worth taking care to avoid a MAC table flush. Bug #891. --- vswitchd/bridge.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index c7559c52b..27d836921 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -1497,7 +1497,6 @@ bridge_flush(struct bridge *br) { COVERAGE_INC(bridge_flush); br->flush = true; - mac_learning_flush(br->ml); } /* Bridge unixctl user interface functions. */ @@ -3969,12 +3968,30 @@ port_del_ifaces(struct port *port, const struct ovsrec_port *cfg) shash_destroy(&new_ifaces); } +/* Expires all MAC learning entries associated with 'port' and forces ofproto + * to revalidate every flow. */ +static void +port_flush_macs(struct port *port) +{ + struct bridge *br = port->bridge; + struct mac_learning *ml = br->ml; + struct mac_entry *mac, *next_mac; + + bridge_flush(br); + LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) { + if (mac->port.p == port) { + mac_learning_expire(ml, mac); + } + } +} + static void port_reconfigure(struct port *port, const struct ovsrec_port *cfg) { const char *detect_mode; struct shash new_ifaces; long long int next_rebalance, miimon_next_update, lacp_priority; + bool need_flush = false; unsigned long *trunks; int vlan; size_t i; @@ -4141,7 +4158,7 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) } if (port->vlan != vlan) { port->vlan = vlan; - bridge_flush(port->bridge); + need_flush = true; } /* Get trunked VLANs. */ @@ -4176,10 +4193,14 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) if (trunks == NULL ? port->trunks != NULL : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) { - bridge_flush(port->bridge); + need_flush = true; } bitmap_free(port->trunks); port->trunks = trunks; + + if (need_flush) { + port_flush_macs(port); + } } static void @@ -4209,11 +4230,12 @@ port_destroy(struct port *port) VLOG_INFO("destroyed port %s on bridge %s", port->name, br->name); + port_flush_macs(port); + netdev_monitor_destroy(port->monitor); bitmap_free(port->trunks); free(port->name); free(port); - bridge_flush(br); } } @@ -4703,6 +4725,7 @@ mirror_reconfigure(struct bridge *br) } if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) { bridge_flush(br); + mac_learning_flush(br->ml); } } @@ -4725,6 +4748,7 @@ mirror_create(struct bridge *br, struct ovsrec_mirror *cfg) VLOG_INFO("created port mirror %s on bridge %s", cfg->name, br->name); bridge_flush(br); + mac_learning_flush(br->ml); br->mirrors[i] = m = xzalloc(sizeof *m); m->bridge = br; @@ -4761,6 +4785,7 @@ mirror_destroy(struct mirror *m) free(m); bridge_flush(br); + mac_learning_flush(br->ml); } } @@ -4900,6 +4925,7 @@ mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) || m->out_port != out_port || m->out_vlan != out_vlan) { bridge_flush(m->bridge); + mac_learning_flush(m->bridge->ml); } shash_swap(&m->src_ports, &src_ports); shash_swap(&m->dst_ports, &dst_ports); -- 2.43.0