X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif-xlate.c;h=e2ac9adbc0e1468782ca30da7c964a9fbc007b8a;hb=dc723c447a797e555d400594133a35b9841eb1de;hp=ffcfdf9be6ff1973ae3d3276aa8e9fa8b668e654;hpb=c56fac1b678101b189e16f0462556c992feeffdc;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index ffcfdf9be..e2ac9adbc 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -58,6 +58,8 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); /* Maximum depth of flow table recursion (due to resubmit actions) in a * flow translation. */ #define MAX_RESUBMIT_RECURSION 64 +#define MAX_INTERNAL_RESUBMITS 1 /* Max resbmits allowed using rules in + internal table. */ /* Maximum number of resubmit actions in a flow translation, whether they are * recursive or not. */ @@ -89,6 +91,9 @@ struct xbridge { bool has_in_band; /* Bridge has in band control? */ bool forward_bpdu; /* Bridge forwards STP BPDUs? */ + /* True if the datapath supports recirculation. */ + bool enable_recirc; + /* True if the datapath supports variable-length * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. * False if the datapath supports only 8-byte (or shorter) userdata. */ @@ -178,6 +183,7 @@ struct xlate_ctx { /* Resubmit statistics, via xlate_table_action(). */ int recurse; /* Current resubmit nesting depth. */ int resubmits; /* Total number of resubmits. */ + bool in_group; /* Currently translating ofgroup, if true. */ uint32_t orig_skb_priority; /* Priority when packet arrived. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ @@ -200,9 +206,11 @@ struct xlate_ctx { * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for * when an input bundle is needed for validation (e.g., mirroring or * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have - * any 'port' structs, so care must be taken when dealing with it. - * The bundle's name and vlan mode are initialized in lookup_input_bundle() */ -static struct xbundle ofpp_none_bundle; + * any 'port' structs, so care must be taken when dealing with it. */ +static struct xbundle ofpp_none_bundle = { + .name = "OFPP_NONE", + .vlan_mode = PORT_VLAN_TRUNK +}; /* Node in 'xport''s 'skb_priorities' map. Used to maintain a map from * 'priority' (the datapath's term for QoS queue) to the dscp bits which all @@ -214,6 +222,72 @@ struct skb_priority_to_dscp { uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */ }; +enum xc_type { + XC_RULE, + XC_BOND, + XC_NETDEV, + XC_NETFLOW, + XC_MIRROR, + XC_LEARN, + XC_NORMAL, + XC_FIN_TIMEOUT, +}; + +/* xlate_cache entries hold enough information to perform the side effects of + * xlate_actions() for a rule, without needing to perform rule translation + * from scratch. The primary usage of these is to submit statistics to objects + * that a flow relates to, although they may be used for other effects as well + * (for instance, refreshing hard timeouts for learned flows). */ +struct xc_entry { + enum xc_type type; + union { + struct rule_dpif *rule; + struct { + struct netdev *tx; + struct netdev *rx; + struct bfd *bfd; + } dev; + struct { + struct netflow *netflow; + struct flow *flow; + ofp_port_t iface; + } nf; + struct { + struct mbridge *mbridge; + mirror_mask_t mirrors; + } mirror; + struct { + struct bond *bond; + struct flow *flow; + uint16_t vid; + } bond; + struct { + struct ofproto_dpif *ofproto; + struct rule_dpif *rule; + } learn; + struct { + struct ofproto_dpif *ofproto; + struct flow *flow; + int vlan; + } normal; + struct { + struct rule_dpif *rule; + uint16_t idle; + uint16_t hard; + } fin; + } u; +}; + +#define XC_ENTRY_FOR_EACH(entry, entries, xcache) \ + entries = xcache->entries; \ + for (entry = ofpbuf_try_pull(&entries, sizeof *entry); \ + entry; \ + entry = ofpbuf_try_pull(&entries, sizeof *entry)) + +struct xlate_cache { + struct ofpbuf entries; +}; + static struct hmap xbridges = HMAP_INITIALIZER(&xbridges); static struct hmap xbundles = HMAP_INITIALIZER(&xbundles); static struct hmap xports = HMAP_INITIALIZER(&xports); @@ -223,10 +297,11 @@ static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len, struct xlate_ctx *); static void xlate_actions__(struct xlate_in *, struct xlate_out *) OVS_REQ_RDLOCK(xlate_rwlock); - static void xlate_normal(struct xlate_ctx *); - static void xlate_report(struct xlate_ctx *, const char *); - static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port, - uint8_t table_id, bool may_packet_in); +static void xlate_normal(struct xlate_ctx *); +static void xlate_report(struct xlate_ctx *, const char *); +static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port, + uint8_t table_id, bool may_packet_in, + bool honor_table_miss); static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn); static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid); static void output_normal(struct xlate_ctx *, const struct xbundle *, @@ -243,6 +318,9 @@ static void clear_skb_priorities(struct xport *); static bool dscp_from_skb_priority(const struct xport *, uint32_t skb_priority, uint8_t *dscp); +static struct xc_entry *xlate_cache_add_entry(struct xlate_cache *xc, + enum xc_type type); + void xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, struct dpif *dpif, struct rule_dpif *miss_rule, @@ -253,6 +331,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, const struct dpif_ipfix *ipfix, const struct netflow *netflow, enum ofp_config_flags frag, bool forward_bpdu, bool has_in_band, + bool enable_recirc, bool variable_length_userdata, size_t max_mpls_depth) { @@ -306,6 +385,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, xbridge->frag = frag; xbridge->miss_rule = miss_rule; xbridge->no_packet_in_rule = no_packet_in_rule; + xbridge->enable_recirc = enable_recirc; xbridge->variable_length_userdata = variable_length_userdata; xbridge->max_mpls_depth = max_mpls_depth; } @@ -664,7 +744,7 @@ xport_get_stp_port(const struct xport *xport) : NULL; } -static enum stp_state +static bool xport_stp_learn_state(const struct xport *xport) { struct stp_port *sp = xport_get_stp_port(xport); @@ -678,13 +758,21 @@ xport_stp_forward_state(const struct xport *xport) return stp_forward_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED); } +static bool +xport_stp_listen_state(const struct xport *xport) +{ + struct stp_port *sp = xport_get_stp_port(xport); + return stp_listen_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED); +} + /* Returns true if STP should process 'flow'. Sets fields in 'wc' that * were used to make the determination.*/ static bool stp_should_process_flow(const struct flow *flow, struct flow_wildcards *wc) { + /* is_stp() also checks dl_type, but dl_type is always set in 'wc'. */ memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); - return eth_addr_equals(flow->dl_dst, eth_addr_stp); + return is_stp(flow); } static void @@ -692,7 +780,7 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet) { struct stp_port *sp = xport_get_stp_port(xport); struct ofpbuf payload = *packet; - struct eth_header *eth = payload.data; + struct eth_header *eth = ofpbuf_data(&payload); /* Sink packets on ports that have STP disabled when the bridge has * STP enabled. */ @@ -701,12 +789,12 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet) } /* Trim off padding on payload. */ - if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) { - payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN; + if (ofpbuf_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) { + ofpbuf_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN); } if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { - stp_received_bpdu(sp, payload.data, payload.size); + stp_received_bpdu(sp, ofpbuf_data(&payload), ofpbuf_size(&payload)); } } @@ -886,8 +974,6 @@ lookup_input_bundle(const struct xbridge *xbridge, ofp_port_t in_port, /* Special-case OFPP_NONE, which a controller may use as the ingress * port for traffic that it is sourcing. */ if (in_port == OFPP_NONE) { - ofpp_none_bundle.name = "OFPP_NONE"; - ofpp_none_bundle.vlan_mode = PORT_VLAN_TRUNK; return &ofpp_none_bundle; } @@ -1122,13 +1208,30 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle, /* Partially configured bundle with no slaves. Drop the packet. */ return; } else if (!out_xbundle->bond) { + ctx->xout->use_recirc = false; xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport, bundle_node); } else { struct ofport_dpif *ofport; + struct xlate_recirc *xr = &ctx->xout->recirc; + struct flow_wildcards *wc = &ctx->xout->wc; + + if (ctx->xbridge->enable_recirc) { + ctx->xout->use_recirc = bond_may_recirc( + out_xbundle->bond, &xr->recirc_id, &xr->hash_basis); + + if (ctx->xout->use_recirc) { + /* Only TCP mode uses recirculation. */ + xr->hash_alg = OVS_HASH_ALG_L4; + bond_update_post_recirc_rules(out_xbundle->bond, false); - ofport = bond_choose_output_slave(out_xbundle->bond, &ctx->xin->flow, - &ctx->xout->wc, vid); + /* Recirculation does not require unmasking hash fields. */ + wc = NULL; + } + } + + ofport = bond_choose_output_slave(out_xbundle->bond, + &ctx->xin->flow, wc, vid); xport = xport_lookup(ofport); if (!xport) { @@ -1136,9 +1239,23 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle, return; } - if (ctx->xin->resubmit_stats) { - bond_account(out_xbundle->bond, &ctx->xin->flow, vid, - ctx->xin->resubmit_stats->n_bytes); + /* If ctx->xout->use_recirc is set, the main thread will handle stats + * accounting for this bond. */ + if (!ctx->xout->use_recirc) { + if (ctx->xin->resubmit_stats) { + bond_account(out_xbundle->bond, &ctx->xin->flow, vid, + ctx->xin->resubmit_stats->n_bytes); + } + if (ctx->xin->xcache) { + struct xc_entry *entry; + struct flow *flow; + + flow = &ctx->xin->flow; + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_BOND); + entry->u.bond.bond = bond_ref(out_xbundle->bond); + entry->u.bond.flow = xmemdup(flow, sizeof *flow); + entry->u.bond.vid = vid; + } } } @@ -1426,6 +1543,15 @@ xlate_normal(struct xlate_ctx *ctx) if (ctx->xin->may_learn) { update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle); } + if (ctx->xin->xcache) { + struct xc_entry *entry; + + /* Save enough info to update mac learning table later. */ + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL); + entry->u.normal.ofproto = ctx->xin->ofproto; + entry->u.normal.flow = xmemdup(flow, sizeof *flow); + entry->u.normal.vlan = vlan; + } /* Determine output bundle. */ ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock); @@ -1483,8 +1609,10 @@ compose_sample_action(const struct xbridge *xbridge, actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS); odp_port = ofp_port_to_odp_port(xbridge, flow->in_port.ofp_port); - pid = dpif_port_get_pid(xbridge->dpif, odp_port); - cookie_offset = odp_put_userspace_action(pid, cookie, cookie_size, odp_actions); + pid = dpif_port_get_pid(xbridge->dpif, odp_port, + flow_hash_5tuple(flow, 0)); + cookie_offset = odp_put_userspace_action(pid, cookie, cookie_size, + odp_actions); nl_msg_end_nested(odp_actions, actions_offset); nl_msg_end_nested(odp_actions, sample_offset); @@ -1684,7 +1812,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26); if (!xport) { xlate_report(ctx, "Nonexistent output port"); @@ -1692,9 +1820,18 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, } else if (xport->config & OFPUTIL_PC_NO_FWD) { xlate_report(ctx, "OFPPC_NO_FWD set, skipping output"); return; - } else if (check_stp && !xport_stp_forward_state(xport)) { - xlate_report(ctx, "STP not in forwarding state, skipping output"); - return; + } else if (check_stp) { + if (is_stp(&ctx->base_flow)) { + if (!xport_stp_listen_state(xport)) { + xlate_report(ctx, "STP not in listening state, " + "skipping bpdu output"); + return; + } + } else if (!xport_stp_forward_state(xport)) { + xlate_report(ctx, "STP not in forwarding state, " + "skipping output"); + return; + } } if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) { @@ -1719,17 +1856,17 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, ctx->xout->slow |= special; } else if (may_receive(peer, ctx)) { if (xport_stp_forward_state(peer)) { - xlate_table_action(ctx, flow->in_port.ofp_port, 0, true); + xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); } else { /* Forwarding is disabled by STP. Let OFPP_NORMAL and the * learning action look at the packet, then drop it. */ struct flow old_base_flow = ctx->base_flow; - size_t old_size = ctx->xout->odp_actions.size; + size_t old_size = ofpbuf_size(&ctx->xout->odp_actions); mirror_mask_t old_mirrors = ctx->xout->mirrors; - xlate_table_action(ctx, flow->in_port.ofp_port, 0, true); + xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); ctx->xout->mirrors = old_mirrors; ctx->base_flow = old_base_flow; - ctx->xout->odp_actions.size = old_size; + ofpbuf_set_size(&ctx->xout->odp_actions, old_size); } } @@ -1743,6 +1880,14 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, bfd_account_rx(peer->bfd, ctx->xin->resubmit_stats); } } + if (ctx->xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV); + entry->u.dev.tx = netdev_ref(xport->netdev); + entry->u.dev.rx = netdev_ref(peer->netdev); + entry->u.dev.bfd = bfd_ref(peer->bfd); + } return; } @@ -1752,7 +1897,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, flow_nw_tos = flow->nw_tos; if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) { - wc->masks.nw_tos |= IP_ECN_MASK; + wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= dscp; } @@ -1775,24 +1920,29 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, if (ctx->xin->resubmit_stats) { netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats); } + if (ctx->xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV); + entry->u.dev.tx = netdev_ref(xport->netdev); + } out_port = odp_port; commit_odp_tunnel_action(flow, &ctx->base_flow, &ctx->xout->odp_actions); flow->tunnel = flow_tnl; /* Restore tunnel metadata */ } else { - ofp_port_t vlandev_port; - odp_port = xport->odp_port; + out_port = odp_port; if (ofproto_has_vlan_splinters(ctx->xbridge->ofproto)) { + ofp_port_t vlandev_port; + wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); - } - vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, ofp_port, - flow->vlan_tci); - if (vlandev_port == ofp_port) { - out_port = odp_port; - } else { - out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port); - flow->vlan_tci = htons(0); + vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, + ofp_port, flow->vlan_tci); + if (vlandev_port != ofp_port) { + out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port); + flow->vlan_tci = htons(0); + } } } @@ -1800,8 +1950,25 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow, &ctx->xout->odp_actions, &ctx->xout->wc); - nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT, - out_port); + + if (ctx->xout->use_recirc) { + struct ovs_action_hash *act_hash; + struct xlate_recirc *xr = &ctx->xout->recirc; + + /* Hash action. */ + act_hash = nl_msg_put_unspec_uninit(&ctx->xout->odp_actions, + OVS_ACTION_ATTR_HASH, + sizeof *act_hash); + act_hash->hash_alg = xr->hash_alg; + act_hash->hash_basis = xr->hash_basis; + + /* Recirc action. */ + nl_msg_put_u32(&ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC, + xr->recirc_id); + } else { + nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT, + out_port); + } ctx->sflow_odp_port = odp_port; ctx->sflow_n_outputs++; @@ -1825,7 +1992,7 @@ static void xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule) { struct rule_dpif *old_rule = ctx->rule; - struct rule_actions *actions; + const struct rule_actions *actions; if (ctx->xin->resubmit_stats) { rule_dpif_credit_stats(rule, ctx->xin->resubmit_stats); @@ -1836,7 +2003,6 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule) ctx->rule = rule; actions = rule_dpif_get_actions(rule); do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx); - rule_actions_unref(actions); ctx->rule = old_rule; ctx->recurse--; } @@ -1846,14 +2012,14 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - if (ctx->recurse >= MAX_RESUBMIT_RECURSION) { + if (ctx->recurse >= MAX_RESUBMIT_RECURSION + MAX_INTERNAL_RESUBMITS) { VLOG_ERR_RL(&rl, "resubmit actions recursed over %d times", MAX_RESUBMIT_RECURSION); - } else if (ctx->resubmits >= MAX_RESUBMITS) { + } else if (ctx->resubmits >= MAX_RESUBMITS + MAX_INTERNAL_RESUBMITS) { VLOG_ERR_RL(&rl, "over %d resubmit actions", MAX_RESUBMITS); - } else if (ctx->xout->odp_actions.size > UINT16_MAX) { + } else if (ofpbuf_size(&ctx->xout->odp_actions) > UINT16_MAX) { VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of actions"); - } else if (ctx->stack.size >= 65536) { + } else if (ofpbuf_size(&ctx->stack) >= 65536) { VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack"); } else { return true; @@ -1863,14 +2029,16 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx) } static void -xlate_table_action(struct xlate_ctx *ctx, - ofp_port_t in_port, uint8_t table_id, bool may_packet_in) +xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id, + bool may_packet_in, bool honor_table_miss) { if (xlate_resubmit_resource_check(ctx)) { ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port; bool skip_wildcards = ctx->xin->skip_wildcards; uint8_t old_table_id = ctx->table_id; struct rule_dpif *rule; + enum rule_dpif_lookup_verdict verdict; + enum ofputil_port_config config = 0; ctx->table_id = table_id; @@ -1878,32 +2046,61 @@ xlate_table_action(struct xlate_ctx *ctx, * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will * have surprising behavior). */ ctx->xin->flow.in_port.ofp_port = in_port; - rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow, - !skip_wildcards ? &ctx->xout->wc : NULL, - table_id, &rule); + verdict = rule_dpif_lookup_from_table(ctx->xbridge->ofproto, + &ctx->xin->flow, + !skip_wildcards + ? &ctx->xout->wc : NULL, + honor_table_miss, + &ctx->table_id, &rule, + ctx->xin->xcache != NULL); ctx->xin->flow.in_port.ofp_port = old_in_port; if (ctx->xin->resubmit_hook) { ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse); } - if (!rule && may_packet_in) { - struct xport *xport; - - /* XXX - * check if table configuration flags - * OFPTC11_TABLE_MISS_CONTROLLER, default. - * OFPTC11_TABLE_MISS_CONTINUE, - * OFPTC11_TABLE_MISS_DROP - * When OF1.0, OFPTC11_TABLE_MISS_CONTINUE is used. What to do? */ - xport = get_ofp_port(ctx->xbridge, ctx->xin->flow.in_port.ofp_port); - choose_miss_rule(xport ? xport->config : 0, - ctx->xbridge->miss_rule, - ctx->xbridge->no_packet_in_rule, &rule); + switch (verdict) { + case RULE_DPIF_LOOKUP_VERDICT_MATCH: + goto match; + case RULE_DPIF_LOOKUP_VERDICT_CONTROLLER: + if (may_packet_in) { + struct xport *xport; + + xport = get_ofp_port(ctx->xbridge, + ctx->xin->flow.in_port.ofp_port); + config = xport ? xport->config : 0; + break; + } + /* Fall through to drop */ + case RULE_DPIF_LOOKUP_VERDICT_DROP: + config = OFPUTIL_PC_NO_PACKET_IN; + break; + case RULE_DPIF_LOOKUP_VERDICT_DEFAULT: + if (!ofproto_dpif_wants_packet_in_on_miss(ctx->xbridge->ofproto)) { + config = OFPUTIL_PC_NO_PACKET_IN; + } + break; + default: + OVS_NOT_REACHED(); } + + choose_miss_rule(config, ctx->xbridge->miss_rule, + ctx->xbridge->no_packet_in_rule, &rule, + ctx->xin->xcache != NULL); + +match: if (rule) { + /* Fill in the cache entry here instead of xlate_recursively + * to make the reference counting more explicit. We take a + * reference in the lookups above if we are going to cache the + * rule. */ + if (ctx->xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_RULE); + entry->u.rule = rule; + } xlate_recursively(ctx, rule); - rule_dpif_unref(rule); } ctx->table_id = old_table_id; @@ -1924,7 +2121,7 @@ xlate_group_bucket(struct xlate_ctx *ctx, const struct ofputil_bucket *bucket) ofpacts_execute_action_set(&action_list, &action_set); ctx->recurse++; - do_xlate_actions(action_list.data, action_list.size, ctx); + do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx); ctx->recurse--; ofpbuf_uninit(&action_set); @@ -1970,7 +2167,7 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) const struct ofputil_bucket *bucket; uint32_t basis; - basis = hash_bytes(ctx->xin->flow.dl_dst, sizeof ctx->xin->flow.dl_dst, 0); + basis = hash_mac(ctx->xin->flow.dl_dst, 0, 0); bucket = group_best_live_bucket(ctx, group, basis); if (bucket) { memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); @@ -1981,6 +2178,8 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) static void xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) { + ctx->in_group = true; + switch (group_dpif_get_type(group)) { case OFPGT11_ALL: case OFPGT11_INDIRECT: @@ -1996,12 +2195,38 @@ xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) OVS_NOT_REACHED(); } group_dpif_release(group); + + ctx->in_group = false; +} + +static bool +xlate_group_resource_check(struct xlate_ctx *ctx) +{ + if (!xlate_resubmit_resource_check(ctx)) { + return false; + } else if (ctx->in_group) { + /* Prevent nested translation of OpenFlow groups. + * + * OpenFlow allows this restriction. We enforce this restriction only + * because, with the current architecture, we would otherwise have to + * take a possibly recursive read lock on the ofgroup rwlock, which is + * unsafe given that POSIX allows taking a read lock to block if there + * is a thread blocked on taking the write lock. Other solutions + * without this restriction are also possible, but seem unwarranted + * given the current limited use of groups. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_ERR_RL(&rl, "cannot recursively translate OpenFlow group"); + return false; + } else { + return true; + } } static bool xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id) { - if (xlate_resubmit_resource_check(ctx)) { + if (xlate_group_resource_check(ctx)) { struct group_dpif *group; bool got_group; @@ -2022,6 +2247,15 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx, { ofp_port_t in_port; uint8_t table_id; + bool may_packet_in = false; + bool honor_table_miss = false; + + if (ctx->rule && rule_dpif_is_internal(ctx->rule)) { + /* Still allow missed packets to be sent to the controller + * if resubmitting from an internal table. */ + may_packet_in = true; + honor_table_miss = true; + } in_port = resubmit->in_port; if (in_port == OFPP_IN_PORT) { @@ -2033,7 +2267,8 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx, table_id = ctx->table_id; } - xlate_table_action(ctx, in_port, table_id, false); + xlate_table_action(ctx, in_port, table_id, may_packet_in, + honor_table_miss); } static void @@ -2076,11 +2311,12 @@ execute_controller_action(struct xlate_ctx *ctx, int len, &ctx->xout->odp_actions, &ctx->xout->wc); - odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data, - ctx->xout->odp_actions.size, NULL); + odp_execute_actions(NULL, packet, false, &md, + ofpbuf_data(&ctx->xout->odp_actions), + ofpbuf_size(&ctx->xout->odp_actions), NULL); pin = xmalloc(sizeof *pin); - pin->up.packet_len = packet->size; + pin->up.packet_len = ofpbuf_size(packet); pin->up.packet = ofpbuf_steal_data(packet); pin->up.reason = reason; pin->up.table_id = ctx->table_id; @@ -2092,8 +2328,22 @@ execute_controller_action(struct xlate_ctx *ctx, int len, pin->controller_id = controller_id; pin->send_len = len; - pin->generated_by_table_miss = (ctx->rule - && rule_dpif_is_table_miss(ctx->rule)); + /* If a rule is a table-miss rule then this is + * a table-miss handled by a table-miss rule. + * + * Else, if rule is internal and has a controller action, + * the later being implied by the rule being processed here, + * then this is a table-miss handled without a table-miss rule. + * + * Otherwise this is not a table-miss. */ + pin->miss_type = OFPROTO_PACKET_IN_NO_MISS; + if (ctx->rule) { + if (rule_dpif_is_table_miss(ctx->rule)) { + pin->miss_type = OFPROTO_PACKET_IN_MISS_FLOW; + } else if (rule_dpif_is_internal(ctx->rule)) { + pin->miss_type = OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW; + } + } ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin); ofpbuf_delete(packet); } @@ -2241,7 +2491,7 @@ xlate_output_action(struct xlate_ctx *ctx, break; case OFPP_TABLE: xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, - 0, may_packet_in); + 0, may_packet_in, true); break; case OFPP_NORMAL: xlate_normal(ctx); @@ -2402,15 +2652,45 @@ xlate_learn_action(struct xlate_ctx *ctx, learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts); ofproto_dpif_flow_mod(ctx->xbridge->ofproto, &fm); ofpbuf_uninit(&ofpacts); + + if (ctx->xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_LEARN); + entry->u.learn.ofproto = ctx->xin->ofproto; + /* Lookup the learned rule, taking a reference on it. The reference + * is released when this cache entry is deleted. */ + rule_dpif_lookup(ctx->xbridge->ofproto, &ctx->xin->flow, NULL, + &entry->u.learn.rule, true); + } +} + +static void +xlate_fin_timeout__(struct rule_dpif *rule, uint16_t tcp_flags, + uint16_t idle_timeout, uint16_t hard_timeout) +{ + if (tcp_flags & (TCP_FIN | TCP_RST)) { + rule_dpif_reduce_timeouts(rule, idle_timeout, hard_timeout); + } } static void xlate_fin_timeout(struct xlate_ctx *ctx, const struct ofpact_fin_timeout *oft) { - if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) { - rule_dpif_reduce_timeouts(ctx->rule, oft->fin_idle_timeout, - oft->fin_hard_timeout); + if (ctx->rule) { + xlate_fin_timeout__(ctx->rule, ctx->xin->tcp_flags, + oft->fin_idle_timeout, oft->fin_hard_timeout); + if (ctx->xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx->xin->xcache, XC_FIN_TIMEOUT); + /* XC_RULE already holds a reference on the rule, none is taken + * here. */ + entry->u.fin.rule = ctx->rule; + entry->u.fin.idle = oft->fin_idle_timeout; + entry->u.fin.hard = oft->fin_hard_timeout; + } } } @@ -2445,7 +2725,7 @@ xlate_sample_action(struct xlate_ctx *ctx, static bool may_receive(const struct xport *xport, struct xlate_ctx *ctx) { - if (xport->config & (eth_addr_equals(ctx->xin->flow.dl_dst, eth_addr_stp) + if (xport->config & (is_stp(&ctx->xin->flow) ? OFPUTIL_PC_NO_RECV_STP : OFPUTIL_PC_NO_RECV)) { return false; @@ -2478,7 +2758,7 @@ xlate_action_set(struct xlate_ctx *ctx) ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub); ofpacts_execute_action_set(&action_list, &ctx->action_set); - do_xlate_actions(action_list.data, action_list.size, ctx); + do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx); ofpbuf_uninit(&action_list); } @@ -2646,15 +2926,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_FIELD: set_field = ofpact_get_SET_FIELD(a); mf = set_field->field; - mf_mask_field_and_prereqs(mf, &wc->masks); /* Set field action only ever overwrites packet's outermost * applicable header fields. Do nothing if no header exists. */ - if ((mf->id != MFF_VLAN_VID || flow->vlan_tci & htons(VLAN_CFI)) - && ((mf->id != MFF_MPLS_LABEL && mf->id != MFF_MPLS_TC) - || eth_type_mpls(flow->dl_type))) { - mf_set_flow_value(mf, &set_field->value, flow); + if (mf->id == MFF_VLAN_VID) { + wc->masks.vlan_tci |= htons(VLAN_CFI); + if (!(flow->vlan_tci & htons(VLAN_CFI))) { + break; + } + } else if ((mf->id == MFF_MPLS_LABEL || mf->id == MFF_MPLS_TC) + /* 'dl_type' is already unwildcarded. */ + && !eth_type_mpls(flow->dl_type)) { + break; } + + mf_mask_field_and_prereqs(mf, &wc->masks); + mf_set_flow_value(mf, &set_field->value, flow); break; case OFPACT_STACK_PUSH: @@ -2754,7 +3041,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, ovs_assert(ctx->table_id < ogt->table_id); xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, - ogt->table_id, true); + ogt->table_id, true, true); break; } @@ -2775,6 +3062,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto, xin->packet = packet; xin->may_learn = packet != NULL; xin->rule = rule; + xin->xcache = NULL; xin->ofpacts = NULL; xin->ofpacts_len = 0; xin->tcp_flags = tcp_flags; @@ -2824,8 +3112,8 @@ xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src) ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub, sizeof dst->odp_actions_stub); - ofpbuf_put(&dst->odp_actions, src->odp_actions.data, - src->odp_actions.size); + ofpbuf_put(&dst->odp_actions, ofpbuf_data(&src->odp_actions), + ofpbuf_size(&src->odp_actions)); } static struct skb_priority_to_dscp * @@ -2870,8 +3158,8 @@ actions_output_to_local_port(const struct xlate_ctx *ctx) const struct nlattr *a; unsigned int left; - NL_ATTR_FOR_EACH_UNSAFE (a, left, ctx->xout->odp_actions.data, - ctx->xout->odp_actions.size) { + NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(&ctx->xout->odp_actions), + ofpbuf_size(&ctx->xout->odp_actions)) { if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT && nl_attr_get_odp_port(a) == local_odp_port) { return true; @@ -2903,7 +3191,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) struct flow *flow = &xin->flow; struct rule_dpif *rule = NULL; - struct rule_actions *actions = NULL; + const struct rule_actions *actions = NULL; enum slow_path_reason special; const struct ofpact *ofpacts; struct xport *in_port; @@ -2950,7 +3238,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) ctx.xbridge = xbridge_lookup(xin->ofproto); if (!ctx.xbridge) { - goto out; + return; } ctx.rule = xin->rule; @@ -2975,19 +3263,28 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) ctx.recurse = 0; ctx.resubmits = 0; + ctx.in_group = false; ctx.orig_skb_priority = flow->skb_priority; ctx.table_id = 0; ctx.exit = false; if (!xin->ofpacts && !ctx.rule) { - rule_dpif_lookup(ctx.xbridge->ofproto, flow, - !xin->skip_wildcards ? wc : NULL, &rule); + ctx.table_id = rule_dpif_lookup(ctx.xbridge->ofproto, flow, + !xin->skip_wildcards ? wc : NULL, + &rule, ctx.xin->xcache != NULL); if (ctx.xin->resubmit_stats) { rule_dpif_credit_stats(rule, ctx.xin->resubmit_stats); } + if (ctx.xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx.xin->xcache, XC_RULE); + entry->u.rule = rule; + } ctx.rule = rule; } xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule); + xout->use_recirc = false; if (xin->ofpacts) { ofpacts = xin->ofpacts; @@ -3019,7 +3316,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) break; case OFPC_FRAG_DROP: - goto out; + return; case OFPC_FRAG_REASM: OVS_NOT_REACHED(); @@ -3034,10 +3331,19 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) } in_port = get_ofp_port(ctx.xbridge, flow->in_port.ofp_port); - if (in_port && in_port->is_tunnel && ctx.xin->resubmit_stats) { - netdev_vport_inc_rx(in_port->netdev, ctx.xin->resubmit_stats); - if (in_port->bfd) { - bfd_account_rx(in_port->bfd, ctx.xin->resubmit_stats); + if (in_port && in_port->is_tunnel) { + if (ctx.xin->resubmit_stats) { + netdev_vport_inc_rx(in_port->netdev, ctx.xin->resubmit_stats); + if (in_port->bfd) { + bfd_account_rx(in_port->bfd, ctx.xin->resubmit_stats); + } + } + if (ctx.xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx.xin->xcache, XC_NETDEV); + entry->u.dev.rx = netdev_ref(in_port->netdev); + entry->u.dev.bfd = bfd_ref(in_port->bfd); } } @@ -3056,7 +3362,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) add_sflow_action(&ctx); add_ipfix_action(&ctx); - sample_actions_len = ctx.xout->odp_actions.size; + sample_actions_len = ofpbuf_size(&ctx.xout->odp_actions); if (tnl_may_send && (!in_port || may_receive(in_port, &ctx))) { do_xlate_actions(ofpacts, ofpacts_len, &ctx); @@ -3064,11 +3370,11 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) /* We've let OFPP_NORMAL and the learning action look at the * packet, so drop it now if forwarding is disabled. */ if (in_port && !xport_stp_forward_state(in_port)) { - ctx.xout->odp_actions.size = sample_actions_len; + ofpbuf_set_size(&ctx.xout->odp_actions, sample_actions_len); } } - if (ctx.action_set.size) { + if (ofpbuf_size(&ctx.action_set)) { xlate_action_set(&ctx); } @@ -3085,7 +3391,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) } } - if (nl_attr_oversized(ctx.xout->odp_actions.size)) { + if (nl_attr_oversized(ofpbuf_size(&ctx.xout->odp_actions))) { /* These datapath actions are too big for a Netlink attribute, so we * can't hand them to the kernel directly. dpif_execute() can execute * them one by one with help, so just mark the result as SLOW_ACTION to @@ -3094,28 +3400,45 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) ctx.xout->slow |= SLOW_ACTION; } - if (ctx.xin->resubmit_stats) { - mirror_update_stats(ctx.xbridge->mbridge, xout->mirrors, - ctx.xin->resubmit_stats->n_packets, - ctx.xin->resubmit_stats->n_bytes); - - if (ctx.xbridge->netflow) { - const struct ofpact *ofpacts; - size_t ofpacts_len; - - ofpacts_len = actions->ofpacts_len; - ofpacts = actions->ofpacts; - if (ofpacts_len == 0 - || ofpacts->type != OFPACT_CONTROLLER - || ofpact_next(ofpacts) < ofpact_end(ofpacts, ofpacts_len)) { - /* Only update netflow if we don't have controller flow. We don't - * report NetFlow expiration messages for such facets because they - * are just part of the control logic for the network, not real - * traffic. */ + if (mbridge_has_mirrors(ctx.xbridge->mbridge)) { + if (ctx.xin->resubmit_stats) { + mirror_update_stats(ctx.xbridge->mbridge, xout->mirrors, + ctx.xin->resubmit_stats->n_packets, + ctx.xin->resubmit_stats->n_bytes); + } + if (ctx.xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx.xin->xcache, XC_MIRROR); + entry->u.mirror.mbridge = mbridge_ref(ctx.xbridge->mbridge); + entry->u.mirror.mirrors = xout->mirrors; + } + } + + if (ctx.xbridge->netflow) { + const struct ofpact *ofpacts = actions->ofpacts; + size_t ofpacts_len = actions->ofpacts_len; + + /* Only update netflow if we don't have controller flow. We don't + * report NetFlow expiration messages for such facets because they + * are just part of the control logic for the network, not real + * traffic. */ + if (ofpacts_len == 0 + || ofpacts->type != OFPACT_CONTROLLER + || ofpact_next(ofpacts) < ofpact_end(ofpacts, ofpacts_len)) { + if (ctx.xin->resubmit_stats) { netflow_flow_update(ctx.xbridge->netflow, flow, xout->nf_output_iface, ctx.xin->resubmit_stats); } + if (ctx.xin->xcache) { + struct xc_entry *entry; + + entry = xlate_cache_add_entry(ctx.xin->xcache, XC_NETFLOW); + entry->u.nf.netflow = netflow_ref(ctx.xbridge->netflow); + entry->u.nf.flow = xmemdup(flow, sizeof *flow); + entry->u.nf.iface = xout->nf_output_iface; + } } } @@ -3140,10 +3463,6 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) wc->masks.tp_src &= htons(UINT8_MAX); wc->masks.tp_dst &= htons(UINT8_MAX); } - -out: - rule_actions_unref(actions); - rule_dpif_unref(rule); } /* Sends 'packet' out 'ofport'. @@ -3155,12 +3474,11 @@ xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) struct xport *xport; struct ofpact_output output; struct flow flow; - union flow_in_port in_port_; ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output); /* Use OFPP_NONE as the in_port to avoid special packet processing. */ - in_port_.ofp_port = OFPP_NONE; - flow_extract(packet, 0, 0, NULL, &in_port_, &flow); + flow_extract(packet, NULL, &flow); + flow.in_port.ofp_port = OFPP_NONE; ovs_rwlock_rdlock(&xlate_rwlock); xport = xport_lookup(ofport); @@ -3176,3 +3494,189 @@ xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) &output.ofpact, sizeof output, packet); } + +struct xlate_cache * +xlate_cache_new(void) +{ + struct xlate_cache *xcache = xmalloc(sizeof *xcache); + + ofpbuf_init(&xcache->entries, 512); + return xcache; +} + +static struct xc_entry * +xlate_cache_add_entry(struct xlate_cache *xcache, enum xc_type type) +{ + struct xc_entry *entry; + + entry = ofpbuf_put_zeros(&xcache->entries, sizeof *entry); + entry->type = type; + + return entry; +} + +static void +xlate_cache_netdev(struct xc_entry *entry, const struct dpif_flow_stats *stats) +{ + if (entry->u.dev.tx) { + netdev_vport_inc_tx(entry->u.dev.tx, stats); + } + if (entry->u.dev.rx) { + netdev_vport_inc_rx(entry->u.dev.rx, stats); + } + if (entry->u.dev.bfd) { + bfd_account_rx(entry->u.dev.bfd, stats); + } +} + +static void +xlate_cache_normal(struct ofproto_dpif *ofproto, struct flow *flow, int vlan) +{ + struct xbridge *xbridge; + struct xbundle *xbundle; + struct flow_wildcards wc; + + xbridge = xbridge_lookup(ofproto); + if (!xbridge) { + return; + } + + xbundle = lookup_input_bundle(xbridge, flow->in_port.ofp_port, false, + NULL); + if (!xbundle) { + return; + } + + update_learning_table(xbridge, flow, &wc, vlan, xbundle); +} + +/* Push stats and perform side effects of flow translation. */ +void +xlate_push_stats(struct xlate_cache *xcache, bool may_learn, + const struct dpif_flow_stats *stats) +{ + struct xc_entry *entry; + struct ofpbuf entries = xcache->entries; + + XC_ENTRY_FOR_EACH (entry, entries, xcache) { + switch (entry->type) { + case XC_RULE: + rule_dpif_credit_stats(entry->u.rule, stats); + break; + case XC_BOND: + bond_account(entry->u.bond.bond, entry->u.bond.flow, + entry->u.bond.vid, stats->n_bytes); + break; + case XC_NETDEV: + xlate_cache_netdev(entry, stats); + break; + case XC_NETFLOW: + netflow_flow_update(entry->u.nf.netflow, entry->u.nf.flow, + entry->u.nf.iface, stats); + break; + case XC_MIRROR: + mirror_update_stats(entry->u.mirror.mbridge, + entry->u.mirror.mirrors, + stats->n_packets, stats->n_bytes); + break; + case XC_LEARN: + if (may_learn) { + struct rule_dpif *rule = entry->u.learn.rule; + + /* Reset the modified time for a rule that is equivalent to + * the currently cached rule. If the rule is not the exact + * rule we have cached, update the reference that we have. */ + entry->u.learn.rule = ofproto_dpif_refresh_rule(rule); + } + break; + case XC_NORMAL: + xlate_cache_normal(entry->u.normal.ofproto, entry->u.normal.flow, + entry->u.normal.vlan); + break; + case XC_FIN_TIMEOUT: + xlate_fin_timeout__(entry->u.fin.rule, stats->tcp_flags, + entry->u.fin.idle, entry->u.fin.hard); + break; + default: + OVS_NOT_REACHED(); + } + } +} + +static void +xlate_dev_unref(struct xc_entry *entry) +{ + if (entry->u.dev.tx) { + netdev_close(entry->u.dev.tx); + } + if (entry->u.dev.rx) { + netdev_close(entry->u.dev.rx); + } + if (entry->u.dev.bfd) { + bfd_unref(entry->u.dev.bfd); + } +} + +static void +xlate_cache_clear_netflow(struct netflow *netflow, struct flow *flow) +{ + netflow_expire(netflow, flow); + netflow_flow_clear(netflow, flow); + netflow_unref(netflow); + free(flow); +} + +void +xlate_cache_clear(struct xlate_cache *xcache) +{ + struct xc_entry *entry; + struct ofpbuf entries; + + if (!xcache) { + return; + } + + XC_ENTRY_FOR_EACH (entry, entries, xcache) { + switch (entry->type) { + case XC_RULE: + rule_dpif_unref(entry->u.rule); + break; + case XC_BOND: + free(entry->u.bond.flow); + bond_unref(entry->u.bond.bond); + break; + case XC_NETDEV: + xlate_dev_unref(entry); + break; + case XC_NETFLOW: + xlate_cache_clear_netflow(entry->u.nf.netflow, entry->u.nf.flow); + break; + case XC_MIRROR: + mbridge_unref(entry->u.mirror.mbridge); + break; + case XC_LEARN: + /* 'u.learn.rule' is the learned rule. */ + rule_dpif_unref(entry->u.learn.rule); + 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 + * has already released it's reference above. */ + break; + default: + OVS_NOT_REACHED(); + } + } + + ofpbuf_clear(&xcache->entries); +} + +void +xlate_cache_delete(struct xlate_cache *xcache) +{ + xlate_cache_clear(xcache); + ofpbuf_uninit(&xcache->entries); + free(xcache); +}