Make mac table update functions part of the mac-learning module, which also helps in figuring what is the minimal set of struct flow fields needed for the update. Use this to change the xlate cache entry for XC_NORMAL to not take a copy of the struct flow, but only save the in_port, dl_src, and some auxiliary fields. This reduces the memory burden of XC_NORMAL by roughly 0.5kb.
Signed-off-by: Jarno Rajahalme <ja...@ovn.org> --- v3: New patch for v3. lib/mac-learning.c | 126 +++++++++++++++++++++++++++++++++++++ lib/mac-learning.h | 4 ++ ofproto/ofproto-dpif-xlate.c | 147 +++++++++---------------------------------- 3 files changed, 159 insertions(+), 118 deletions(-) diff --git a/lib/mac-learning.c b/lib/mac-learning.c index 5a2ccba..5509f22 100644 --- a/lib/mac-learning.c +++ b/lib/mac-learning.c @@ -335,6 +335,132 @@ mac_learning_insert(struct mac_learning *ml, return e; } +/* Checks whether a MAC learning update is necessary for MAC learning table + * 'ml' given that a packet matching 'src' was received on 'in_port' in 'vlan', + * and given that the packet was gratuitous ARP if 'is_gratuitous_arp' is + * 'true' and 'in_port' is a bond port if 'is_bond' is 'true'. + * + * Most packets processed through the MAC learning table do not actually + * change it in any way. This function requires only a read lock on the MAC + * learning table, so it is much cheaper in this common case. + * + * Keep the code here synchronized with that in update_learning_table__() + * below. */ +static bool +is_mac_learning_update_needed(const struct mac_learning *ml, + struct eth_addr src, int vlan, + bool is_gratuitous_arp, bool is_bond, + void *in_port) + OVS_REQ_RDLOCK(ml->rwlock) +{ + struct mac_entry *mac; + + if (!mac_learning_may_learn(ml, src, vlan)) { + return false; + } + + mac = mac_learning_lookup(ml, src, vlan); + if (!mac || mac_entry_age(ml, mac)) { + return true; + } + + if (is_gratuitous_arp) { + /* We don't want to learn from gratuitous ARP packets that are + * reflected back over bond slaves so we lock the learning table. For + * more detail, see the bigger comment in update_learning_table__(). */ + if (!is_bond) { + return true; /* Need to set the gratuitous ARP lock. */ + } else if (mac_entry_is_grat_arp_locked(mac)) { + return false; + } + } + + return mac_entry_get_port(ml, mac) != in_port /* ofbundle */; +} + +/* Updates MAC learning table 'ml' given that a packet matching 'src' was + * received on 'in_port' in 'vlan', and given that the packet was gratuitous + * ARP if 'is_gratuitous_arp' is 'true' and 'in_port' is a bond port if + * 'is_bond' is 'true'. + * + * This code repeats all the checks in is_mac_learning_update_needed() because + * the lock was released between there and here and thus the MAC learning state + * could have changed. + * + * Returns 'true' if 'ml' was updated, 'false' otherwise. + * + * Keep the code here synchronized with that in is_mac_learning_update_needed() + * above. */ +static bool +update_learning_table__(struct mac_learning *ml, struct eth_addr src, + int vlan, bool is_gratuitous_arp, bool is_bond, + void *in_port) + OVS_REQ_WRLOCK(ml->rwlock) +{ + struct mac_entry *mac; + + if (!mac_learning_may_learn(ml, src, vlan)) { + return false; + } + + mac = mac_learning_insert(ml, src, vlan); + if (is_gratuitous_arp) { + /* Gratuitous ARP packets received over non-bond interfaces could be + * reflected back over bond slaves. We don't want to learn from these + * reflected packets, so we lock each entry for which a gratuitous ARP + * packet was received over a non-bond interface and refrain from + * learning from gratuitous ARP packets that arrive over bond + * interfaces for this entry while the lock is in effect. See + * vswitchd/INTERNALS for more in-depth discussion on this topic. */ + if (!is_bond) { + mac_entry_set_grat_arp_lock(mac); + } else if (mac_entry_is_grat_arp_locked(mac)) { + return false; + } + } + + if (mac_entry_get_port(ml, mac) != in_port) { + mac_entry_set_port(ml, mac, in_port); + return true; + } + return false; +} + +/* Updates MAC learning table 'ml' given that a packet matching 'src' was + * received on 'in_port' in 'vlan', and given that the packet was gratuitous + * ARP if 'is_gratuitous_arp' is 'true' and 'in_port' is a bond port if + * 'is_bond' is 'true'. + * + * Returns 'true' if 'ml' was updated, 'false' otherwise. */ +bool +mac_learning_update(struct mac_learning *ml, struct eth_addr src, + int vlan, bool is_gratuitous_arp, bool is_bond, + void *in_port) + OVS_EXCLUDED(ml->rwlock) +{ + bool need_update; + bool updated = false; + + /* Don't learn the OFPP_NONE port. */ + if (in_port != NULL) { + /* First try the common case: no change to MAC learning table. */ + ovs_rwlock_rdlock(&ml->rwlock); + need_update = is_mac_learning_update_needed(ml, src, vlan, + is_gratuitous_arp, is_bond, + in_port); + ovs_rwlock_unlock(&ml->rwlock); + + if (need_update) { + /* Slow path: MAC learning table might need an update. */ + ovs_rwlock_wrlock(&ml->rwlock); + updated = update_learning_table__(ml, src, vlan, is_gratuitous_arp, + is_bond, in_port); + ovs_rwlock_unlock(&ml->rwlock); + } + } + return updated; +} + /* Looks up MAC 'dst' for VLAN 'vlan' in 'ml' and returns the associated MAC * learning entry, if any. */ struct mac_entry * diff --git a/lib/mac-learning.h b/lib/mac-learning.h index d09e895..d380690 100644 --- a/lib/mac-learning.h +++ b/lib/mac-learning.h @@ -207,6 +207,10 @@ struct mac_entry *mac_learning_insert(struct mac_learning *ml, const struct eth_addr src, uint16_t vlan) OVS_REQ_WRLOCK(ml->rwlock); +bool mac_learning_update(struct mac_learning *ml, struct eth_addr src, + int vlan, bool is_gratuitous_arp, bool is_bond, + void *in_port) + OVS_EXCLUDED(ml->rwlock); /* Lookup. */ struct mac_entry *mac_learning_lookup(const struct mac_learning *ml, diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 74e3387..e4a0c52 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -496,8 +496,10 @@ struct xc_entry { } learn; struct { struct ofproto_dpif *ofproto; - struct flow *flow; + ofp_port_t in_port; + struct eth_addr dl_src; int vlan; + bool is_gratuitous_arp; } normal; struct { struct rule_dpif *rule; @@ -2048,122 +2050,25 @@ is_admissible(struct xlate_ctx *ctx, struct xport *in_port, return true; } -/* Checks whether a MAC learning update is necessary for MAC learning table - * 'ml' given that a packet matching 'flow' was received on 'in_xbundle' in - * 'vlan'. - * - * Most packets processed through the MAC learning table do not actually - * change it in any way. This function requires only a read lock on the MAC - * learning table, so it is much cheaper in this common case. - * - * Keep the code here synchronized with that in update_learning_table__() - * below. */ -static bool -is_mac_learning_update_needed(const struct mac_learning *ml, - const struct flow *flow, - struct flow_wildcards *wc, - int vlan, struct xbundle *in_xbundle) -OVS_REQ_RDLOCK(ml->rwlock) -{ - struct mac_entry *mac; - - if (!mac_learning_may_learn(ml, flow->dl_src, vlan)) { - return false; - } - - mac = mac_learning_lookup(ml, flow->dl_src, vlan); - if (!mac || mac_entry_age(ml, mac)) { - return true; - } - - if (is_gratuitous_arp(flow, wc)) { - /* Gratuitous ARP packets received over non-bond interfaces could be - * reflected back over bond slaves. We don't want to learn from these - * reflected packets, so we lock each entry for which a gratuitous ARP - * packet was received over a non-bond interface and refrain from - * learning from gratuitous ARP packets that arrive over bond - * interfaces for this entry while the lock is in effect. See - * vswitchd/INTERNALS for more in-depth discussion on this topic. */ - if (!in_xbundle->bond) { - return true; - } else if (mac_entry_is_grat_arp_locked(mac)) { - return false; - } - } - - return mac_entry_get_port(ml, mac) != in_xbundle->ofbundle; -} - - -/* Updates MAC learning table 'ml' given that a packet matching 'flow' was - * received on 'in_xbundle' in 'vlan'. - * - * This code repeats all the checks in is_mac_learning_update_needed() because - * the lock was released between there and here and thus the MAC learning state - * could have changed. - * - * Keep the code here synchronized with that in is_mac_learning_update_needed() - * above. */ static void -update_learning_table__(const struct xbridge *xbridge, - const struct flow *flow, struct flow_wildcards *wc, - int vlan, struct xbundle *in_xbundle) -OVS_REQ_WRLOCK(xbridge->ml->rwlock) +update_learning_table(const struct xbridge *xbridge, + struct xbundle *in_xbundle, struct eth_addr dl_src, + int vlan, bool is_grat_arp) { - struct mac_entry *mac; - - if (!mac_learning_may_learn(xbridge->ml, flow->dl_src, vlan)) { + if (in_xbundle == &ofpp_none_bundle) { return; } - mac = mac_learning_insert(xbridge->ml, flow->dl_src, vlan); - if (is_gratuitous_arp(flow, wc)) { - /* We don't want to learn from gratuitous ARP packets that are - * reflected back over bond slaves so we lock the learning table. */ - if (!in_xbundle->bond) { - mac_entry_set_grat_arp_lock(mac); - } else if (mac_entry_is_grat_arp_locked(mac)) { - return; - } - } - - if (mac_entry_get_port(xbridge->ml, mac) != in_xbundle->ofbundle) { + if (mac_learning_update(xbridge->ml, dl_src, vlan, is_grat_arp, + in_xbundle->bond != NULL, in_xbundle->ofbundle)) { /* The log messages here could actually be useful in debugging, * so keep the rate limit relatively high. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is " "on port %s in VLAN %d", - xbridge->name, ETH_ADDR_ARGS(flow->dl_src), - in_xbundle->name, vlan); - - mac_entry_set_port(xbridge->ml, mac, in_xbundle->ofbundle); - } -} - -static void -update_learning_table(const struct xbridge *xbridge, - const struct flow *flow, struct flow_wildcards *wc, - int vlan, struct xbundle *in_xbundle) -{ - bool need_update; - - /* Don't learn the OFPP_NONE port. */ - if (in_xbundle == &ofpp_none_bundle) { - return; - } - - /* First try the common case: no change to MAC learning table. */ - ovs_rwlock_rdlock(&xbridge->ml->rwlock); - need_update = is_mac_learning_update_needed(xbridge->ml, flow, wc, vlan, - in_xbundle); - ovs_rwlock_unlock(&xbridge->ml->rwlock); - - if (need_update) { - /* Slow path: MAC learning table might need an update. */ - ovs_rwlock_wrlock(&xbridge->ml->rwlock); - update_learning_table__(xbridge, flow, wc, vlan, in_xbundle); - ovs_rwlock_unlock(&xbridge->ml->rwlock); + xbridge->name, ETH_ADDR_ARGS(dl_src), in_xbundle->name, + vlan); } } @@ -2485,17 +2390,21 @@ xlate_normal(struct xlate_ctx *ctx) } /* Learn source MAC. */ + bool is_grat_arp = is_gratuitous_arp(flow, wc); if (ctx->xin->may_learn) { - update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle); + update_learning_table(ctx->xbridge, in_xbundle, flow->dl_src, vlan, + is_grat_arp); } - if (ctx->xin->xcache) { + if (ctx->xin->xcache && in_xbundle != &ofpp_none_bundle) { struct xc_entry *entry; - /* Save enough info to update mac learning table later. */ + /* Save just enough info to update mac learning table later. */ entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL); entry->u.normal.ofproto = ctx->xbridge->ofproto; - entry->u.normal.flow = xmemdup(flow, sizeof *flow); + entry->u.normal.in_port = flow->in_port.ofp_port; + entry->u.normal.dl_src = flow->dl_src; entry->u.normal.vlan = vlan; + entry->u.normal.is_gratuitous_arp = is_grat_arp; } /* Determine output bundle. */ @@ -5805,25 +5714,25 @@ xlate_cache_netdev(struct xc_entry *entry, const struct dpif_flow_stats *stats) } static void -xlate_cache_normal(struct ofproto_dpif *ofproto, struct flow *flow, int vlan) +xlate_mac_learning_update(const struct ofproto_dpif *ofproto, + ofp_port_t in_port, struct eth_addr dl_src, + int vlan, bool is_grat_arp) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xbridge *xbridge; struct xbundle *xbundle; - struct flow_wildcards wc; xbridge = xbridge_lookup(xcfg, ofproto); if (!xbridge) { return; } - xbundle = lookup_input_bundle(xbridge, flow->in_port.ofp_port, false, - NULL); + xbundle = lookup_input_bundle(xbridge, in_port, false, NULL); if (!xbundle) { return; } - update_learning_table(xbridge, flow, &wc, vlan, xbundle); + update_learning_table(xbridge, xbundle, dl_src, vlan, is_grat_arp); } /* Push stats and perform side effects of flow translation. */ @@ -5864,8 +5773,11 @@ xlate_push_stats(struct xlate_cache *xcache, ofproto_dpif_flow_mod(entry->u.learn.ofproto, entry->u.learn.fm); break; case XC_NORMAL: - xlate_cache_normal(entry->u.normal.ofproto, entry->u.normal.flow, - entry->u.normal.vlan); + xlate_mac_learning_update(entry->u.normal.ofproto, + entry->u.normal.in_port, + entry->u.normal.dl_src, + entry->u.normal.vlan, + entry->u.normal.is_gratuitous_arp); break; case XC_FIN_TIMEOUT: xlate_fin_timeout__(entry->u.fin.rule, stats->tcp_flags, @@ -5941,7 +5853,6 @@ xlate_cache_clear(struct xlate_cache *xcache) ofpbuf_delete(entry->u.learn.ofpacts); break; case XC_NORMAL: - free(entry->u.normal.flow); break; case XC_FIN_TIMEOUT: /* 'u.fin.rule' is always already held as a XC_RULE, which -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev