X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=9ce60ae26ccb8d61419de416ce4309a8359794d9;hb=c06bba01302e3dc1ec7808024bc37ce90956b49e;hp=0aae004b4b56080c31dceb8ac749588636e7b52a;hpb=4b23aebfca469739f6c4b03ad1c7c7bab9e902ba;p=openvswitch diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 0aae004b..9ce60ae2 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -88,7 +88,7 @@ struct rule_dpif { * * - Do not include packet or bytes that can be obtained from any facet's * packet_count or byte_count member or that can be obtained from the - * datapath by, e.g., dpif_flow_get() for any facet. + * datapath by, e.g., dpif_flow_get() for any subfacet. */ uint64_t packet_count; /* Number of packets received. */ uint64_t byte_count; /* Number of bytes received. */ @@ -106,6 +106,15 @@ static struct rule_dpif *rule_dpif_cast(const struct rule *rule) static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *, const struct flow *, uint8_t table); +static void flow_push_stats(const struct rule_dpif *, const struct flow *, + uint64_t packets, uint64_t bytes, + long long int used); + +static uint32_t rule_calculate_tag(const struct flow *, + const struct flow_wildcards *, + uint32_t basis); +static void rule_invalidate(const struct rule_dpif *); + #define MAX_MIRRORS 32 typedef uint32_t mirror_mask_t; #define MIRROR_MASK_C(X) UINT32_C(X) @@ -125,12 +134,17 @@ struct ofmirror { struct ofbundle *out; /* Output port or NULL. */ int out_vlan; /* Output VLAN or -1. */ mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */ + + /* Counters. */ + int64_t packet_count; /* Number of packets sent. */ + int64_t byte_count; /* Number of bytes sent. */ }; static void mirror_destroy(struct ofmirror *); +static void update_mirror_stats(struct ofproto_dpif *ofproto, + mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes); -/* A group of one or more OpenFlow ports. */ -#define OFBUNDLE_FLOOD ((struct ofbundle *) 1) struct ofbundle { struct ofproto_dpif *ofproto; /* Owning ofproto. */ struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */ @@ -162,6 +176,8 @@ static void bundle_destroy(struct ofbundle *); static void bundle_del_port(struct ofport_dpif *); static void bundle_run(struct ofbundle *); static void bundle_wait(struct ofbundle *); +static struct ofport_dpif *lookup_input_bundle(struct ofproto_dpif *, + uint16_t in_port, bool warn); static void stp_run(struct ofproto_dpif *ofproto); static void stp_wait(struct ofproto_dpif *ofproto); @@ -204,6 +220,7 @@ struct action_xlate_ctx { bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ uint16_t nf_output_iface; /* Output interface index for NetFlow. */ + mirror_mask_t mirrors; /* Bitmap of associated mirrors. */ /* xlate_actions() initializes and uses these members, but the client has no * reason to look at them. */ @@ -220,48 +237,72 @@ struct action_xlate_ctx { static void action_xlate_ctx_init(struct action_xlate_ctx *, struct ofproto_dpif *, const struct flow *, - const struct ofpbuf *); + ovs_be16 initial_tci, const struct ofpbuf *); static struct ofpbuf *xlate_actions(struct action_xlate_ctx *, const union ofp_action *in, size_t n_in); -/* An exact-match instantiation of an OpenFlow flow. */ +/* An exact-match instantiation of an OpenFlow flow. + * + * A facet associates a "struct flow", which represents the Open vSwitch + * userspace idea of an exact-match flow, with one or more subfacets. Each + * subfacet tracks the datapath's idea of the exact-match flow equivalent to + * the facet. When the kernel module (or other dpif implementation) and Open + * vSwitch userspace agree on the definition of a flow key, there is exactly + * one subfacet per facet. If the dpif implementation supports more-specific + * flow matching than userspace, however, a facet can have more than one + * subfacet, each of which corresponds to some distinction in flow that + * userspace simply doesn't understand. + * + * Flow expiration works in terms of subfacets, so a facet must have at least + * one subfacet or it will never expire, leaking memory. */ struct facet { + /* Owners. */ + struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */ + struct list list_node; /* In owning rule's 'facets' list. */ + struct rule_dpif *rule; /* Owning rule. */ + + /* Owned data. */ + struct list subfacets; long long int used; /* Time last used; time created if not used. */ + /* Key. */ + struct flow flow; + /* These statistics: * * - Do include packets and bytes sent "by hand", e.g. with * dpif_execute(). * * - Do include packets and bytes that were obtained from the datapath - * when its statistics were reset (e.g. dpif_flow_put() with + * when a subfacet's statistics were reset (e.g. dpif_flow_put() with * DPIF_FP_ZERO_STATS). + * + * - Do not include packets or bytes that can be obtained from the + * datapath for any existing subfacet. */ uint64_t packet_count; /* Number of packets received. */ uint64_t byte_count; /* Number of bytes received. */ - uint64_t dp_packet_count; /* Last known packet count in the datapath. */ - uint64_t dp_byte_count; /* Last known byte count in the datapath. */ - - uint64_t rs_packet_count; /* Packets pushed to resubmit children. */ - uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */ - long long int rs_used; /* Used time pushed to resubmit children. */ + /* Resubmit statistics. */ + uint64_t prev_packet_count; /* Number of packets from last stats push. */ + uint64_t prev_byte_count; /* Number of bytes from last stats push. */ + long long int prev_used; /* Used time from last stats push. */ + /* Accounting. */ uint64_t accounted_bytes; /* Bytes processed by facet_account(). */ + struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ - struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */ - struct list list_node; /* In owning rule's 'facets' list. */ - struct rule_dpif *rule; /* Owning rule. */ - struct flow flow; /* Exact-match flow. */ - bool installed; /* Installed in datapath? */ - bool may_install; /* True ordinarily; false if actions must - * be reassessed for every packet. */ + /* Properties of datapath actions. + * + * Every subfacet has its own actions because actions can differ slightly + * between splintered and non-splintered subfacets due to the VLAN tag + * being initially different (present vs. absent). All of them have these + * properties in common so we just store one copy of them here. */ + bool may_install; /* Reassess actions for every packet? */ bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ - size_t actions_len; /* Number of bytes in actions[]. */ - struct nlattr *actions; /* Datapath actions. */ - tag_type tags; /* Tags. */ - struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ + tag_type tags; /* Tags that would require revalidation. */ + mirror_mask_t mirrors; /* Bitmap of dependent mirrors. */ }; static struct facet *facet_create(struct rule_dpif *, const struct flow *); @@ -278,38 +319,76 @@ static bool execute_controller_action(struct ofproto_dpif *, const struct nlattr *odp_actions, size_t actions_len, struct ofpbuf *packet); -static void facet_execute(struct ofproto_dpif *, struct facet *, - struct ofpbuf *packet); - -static int facet_put__(struct ofproto_dpif *, struct facet *, - const struct nlattr *actions, size_t actions_len, - struct dpif_flow_stats *); -static void facet_install(struct ofproto_dpif *, struct facet *, - bool zero_stats); -static void facet_uninstall(struct ofproto_dpif *, struct facet *); + static void facet_flush_stats(struct ofproto_dpif *, struct facet *); -static void facet_make_actions(struct ofproto_dpif *, struct facet *, - const struct ofpbuf *packet); static void facet_update_time(struct ofproto_dpif *, struct facet *, long long int used); -static void facet_update_stats(struct ofproto_dpif *, struct facet *, - const struct dpif_flow_stats *); static void facet_reset_counters(struct facet *); -static void facet_reset_dp_stats(struct facet *, struct dpif_flow_stats *); static void facet_push_stats(struct facet *); static void facet_account(struct ofproto_dpif *, struct facet *); static bool facet_is_controller_flow(struct facet *); -static void flow_push_stats(const struct rule_dpif *, - struct flow *, uint64_t packets, uint64_t bytes, - long long int used); +/* A dpif flow and actions associated with a facet. + * + * See also the large comment on struct facet. */ +struct subfacet { + /* Owners. */ + struct hmap_node hmap_node; /* In struct ofproto_dpif 'subfacets' list. */ + struct list list_node; /* In struct facet's 'facets' list. */ + struct facet *facet; /* Owning facet. */ + + /* Key. + * + * To save memory in the common case, 'key' is NULL if 'key_fitness' is + * ODP_FIT_PERFECT, that is, odp_flow_key_from_flow() can accurately + * regenerate the ODP flow key from ->facet->flow. */ + enum odp_key_fitness key_fitness; + struct nlattr *key; + int key_len; -static uint32_t rule_calculate_tag(const struct flow *, - const struct flow_wildcards *, - uint32_t basis); -static void rule_invalidate(const struct rule_dpif *); + long long int used; /* Time last used; time created if not used. */ + + uint64_t dp_packet_count; /* Last known packet count in the datapath. */ + uint64_t dp_byte_count; /* Last known byte count in the datapath. */ + + /* Datapath actions. + * + * These should be essentially identical for every subfacet in a facet, but + * may differ in trivial ways due to VLAN splinters. */ + size_t actions_len; /* Number of bytes in actions[]. */ + struct nlattr *actions; /* Datapath actions. */ + + bool installed; /* Installed in datapath? */ + + /* This value is normally the same as ->facet->flow.vlan_tci. Only VLAN + * splinters can cause it to differ. This value should be removed when + * the VLAN splinters feature is no longer needed. */ + ovs_be16 initial_tci; /* Initial VLAN TCI value. */ +}; + +static struct subfacet *subfacet_create(struct ofproto_dpif *, struct facet *, + enum odp_key_fitness, + const struct nlattr *key, + size_t key_len, ovs_be16 initial_tci); +static struct subfacet *subfacet_find(struct ofproto_dpif *, + const struct nlattr *key, size_t key_len, + const struct flow *); +static void subfacet_destroy(struct ofproto_dpif *, struct subfacet *); +static void subfacet_destroy__(struct ofproto_dpif *, struct subfacet *); +static void subfacet_reset_dp_stats(struct subfacet *, + struct dpif_flow_stats *); +static void subfacet_update_time(struct ofproto_dpif *, struct subfacet *, + long long int used); +static void subfacet_update_stats(struct ofproto_dpif *, struct subfacet *, + const struct dpif_flow_stats *); +static void subfacet_make_actions(struct ofproto_dpif *, struct subfacet *, + const struct ofpbuf *packet); +static int subfacet_install(struct ofproto_dpif *, struct subfacet *, + const struct nlattr *actions, size_t actions_len, + struct dpif_flow_stats *); +static void subfacet_uninstall(struct ofproto_dpif *, struct subfacet *); struct ofport_dpif { struct ofport up; @@ -322,11 +401,54 @@ struct ofport_dpif { uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */ bool may_enable; /* May be enabled in bonds. */ + /* Spanning tree. */ struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */ enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ long long int stp_state_entered; + + struct hmap priorities; /* Map of attached 'priority_to_dscp's. */ + + /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device + * drivers in old versions of Linux that do not properly support VLANs when + * VLAN devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ + uint16_t realdev_ofp_port; + int vlandev_vid; +}; + +/* Node in 'ofport_dpif''s 'priorities' map. Used to maintain a map from + * 'priority' (the datapath's term for QoS queue) to the dscp bits which all + * traffic egressing the 'ofport' with that priority should be marked with. */ +struct priority_to_dscp { + struct hmap_node hmap_node; /* Node in 'ofport_dpif''s 'priorities' map. */ + uint32_t priority; /* Priority of this queue (see struct flow). */ + + uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */ +}; + +/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device drivers + * in old versions of Linux that do not properly support VLANs when VLAN + * devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ +struct vlan_splinter { + struct hmap_node realdev_vid_node; + struct hmap_node vlandev_node; + uint16_t realdev_ofp_port; + uint16_t vlandev_ofp_port; + int vid; }; +static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *, + uint32_t realdev, ovs_be16 vlan_tci); +static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *, + uint16_t vlandev, int *vid); +static void vsp_remove(struct ofport_dpif *); +static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid); + static struct ofport_dpif * ofport_dpif_cast(const struct ofport *ofport) { @@ -337,6 +459,7 @@ ofport_dpif_cast(const struct ofport *ofport) static void port_run(struct ofport_dpif *); static void port_wait(struct ofport_dpif *); static int set_cfm(struct ofport *, const struct cfm_settings *); +static void ofport_clear_priorities(struct ofport_dpif *); struct dpif_completion { struct list list_node; @@ -375,6 +498,7 @@ struct ofproto_dpif { /* Facets. */ struct hmap facets; + struct hmap subfacets; /* Revalidation. */ struct table_dpif tables[N_TABLES]; @@ -389,6 +513,10 @@ struct ofproto_dpif { /* Spanning tree. */ struct stp *stp; long long int stp_last_tick; + + /* VLAN splinters. */ + struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */ + struct hmap vlandev_map; /* vlandev -> (realdev,vid). */ }; /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only @@ -423,12 +551,16 @@ static void handle_miss_upcalls(struct ofproto_dpif *, /* Flow expiration. */ static int expire(struct ofproto_dpif *); +/* NetFlow. */ +static void send_netflow_active_timeouts(struct ofproto_dpif *); + /* Utilities. */ -static int send_packet(struct ofproto_dpif *, uint32_t odp_port, - const struct ofpbuf *packet); +static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet); static size_t compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions, const struct flow *, uint32_t odp_port); +static void add_mirror_actions(struct action_xlate_ctx *ctx, + const struct flow *flow); /* Global variables. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -518,6 +650,7 @@ construct(struct ofproto *ofproto_, int *n_tablesp) timer_set_duration(&ofproto->next_expiration, 1000); hmap_init(&ofproto->facets); + hmap_init(&ofproto->subfacets); for (i = 0; i < N_TABLES; i++) { struct table_dpif *table = &ofproto->tables[i]; @@ -535,6 +668,9 @@ construct(struct ofproto *ofproto_, int *n_tablesp) ofproto->has_bundle_action = false; + hmap_init(&ofproto->vlandev_map); + hmap_init(&ofproto->realdev_vid_map); + *n_tablesp = N_TABLES; return 0; } @@ -580,6 +716,10 @@ destruct(struct ofproto *ofproto_) mac_learning_destroy(ofproto->ml); hmap_destroy(&ofproto->facets); + hmap_destroy(&ofproto->subfacets); + + hmap_destroy(&ofproto->vlandev_map); + hmap_destroy(&ofproto->realdev_vid_map); dpif_close(ofproto->dpif); } @@ -628,7 +768,9 @@ run(struct ofproto *ofproto_) } if (ofproto->netflow) { - netflow_run(ofproto->netflow); + if (netflow_run(ofproto->netflow)) { + send_netflow_active_timeouts(ofproto); + } } if (ofproto->sflow) { dpif_sflow_run(ofproto->sflow); @@ -691,6 +833,9 @@ wait(struct ofproto *ofproto_) HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { bundle_wait(bundle); } + if (ofproto->netflow) { + netflow_wait(ofproto->netflow); + } mac_learning_wait(ofproto->ml); stp_wait(ofproto); if (ofproto->need_revalidate) { @@ -713,9 +858,13 @@ flush(struct ofproto *ofproto_) * bother trying to uninstall it. There is no point in uninstalling it * individually since we are about to blow away all the facets with * dpif_flow_flush(). */ - facet->installed = false; - facet->dp_packet_count = 0; - facet->dp_byte_count = 0; + struct subfacet *subfacet; + + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + subfacet->installed = false; + subfacet->dp_packet_count = 0; + subfacet->dp_byte_count = 0; + } facet_remove(ofproto, facet); } dpif_flow_flush(ofproto->dpif); @@ -754,24 +903,6 @@ get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots) htonll(s.n_hit + ofproto->n_matches)); } -static int -set_netflow(struct ofproto *ofproto_, - const struct netflow_options *netflow_options) -{ - struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - - if (netflow_options) { - if (!ofproto->netflow) { - ofproto->netflow = netflow_create(); - } - return netflow_set_options(ofproto->netflow, netflow_options); - } else { - netflow_destroy(ofproto->netflow); - ofproto->netflow = NULL; - return 0; - } -} - static struct ofport * port_alloc(void) { @@ -800,6 +931,9 @@ port_construct(struct ofport *port_) port->may_enable = true; port->stp_port = NULL; port->stp_state = STP_DISABLED; + hmap_init(&port->priorities); + port->realdev_ofp_port = 0; + port->vlandev_vid = 0; if (ofproto->sflow) { dpif_sflow_add_port(ofproto->sflow, port->odp_port, @@ -821,6 +955,9 @@ port_destruct(struct ofport *port_) if (ofproto->sflow) { dpif_sflow_del_port(ofproto->sflow, port->odp_port); } + + ofport_clear_priorities(port); + hmap_destroy(&port->priorities); } static void @@ -950,8 +1087,7 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " "with unknown MAC", ofproto->up.name, port_num); } else { - send_packet(ofproto_dpif_cast(ofport->up.ofproto), - ofport->odp_port, pkt); + send_packet(ofport, pkt); } } ofpbuf_delete(pkt); @@ -1172,6 +1308,82 @@ stp_process_packet(const struct ofport_dpif *ofport, } } +static struct priority_to_dscp * +get_priority(const struct ofport_dpif *ofport, uint32_t priority) +{ + struct priority_to_dscp *pdscp; + uint32_t hash; + + hash = hash_int(priority, 0); + HMAP_FOR_EACH_IN_BUCKET (pdscp, hmap_node, hash, &ofport->priorities) { + if (pdscp->priority == priority) { + return pdscp; + } + } + return NULL; +} + +static void +ofport_clear_priorities(struct ofport_dpif *ofport) +{ + struct priority_to_dscp *pdscp, *next; + + HMAP_FOR_EACH_SAFE (pdscp, next, hmap_node, &ofport->priorities) { + hmap_remove(&ofport->priorities, &pdscp->hmap_node); + free(pdscp); + } +} + +static int +set_queues(struct ofport *ofport_, + const struct ofproto_port_queue *qdscp_list, + size_t n_qdscp) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct hmap new = HMAP_INITIALIZER(&new); + size_t i; + + for (i = 0; i < n_qdscp; i++) { + struct priority_to_dscp *pdscp; + uint32_t priority; + uint8_t dscp; + + dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK; + if (dpif_queue_to_priority(ofproto->dpif, qdscp_list[i].queue, + &priority)) { + continue; + } + + pdscp = get_priority(ofport, priority); + if (pdscp) { + hmap_remove(&ofport->priorities, &pdscp->hmap_node); + } else { + pdscp = xmalloc(sizeof *pdscp); + pdscp->priority = priority; + pdscp->dscp = dscp; + ofproto->need_revalidate = true; + } + + if (pdscp->dscp != dscp) { + pdscp->dscp = dscp; + ofproto->need_revalidate = true; + } + + hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0)); + } + + if (!hmap_is_empty(&ofport->priorities)) { + ofport_clear_priorities(ofport); + ofproto->need_revalidate = true; + } + + hmap_swap(&new, &ofport->priorities); + hmap_destroy(&new); + + return 0; +} + /* Bundles. */ /* Expires all MAC learning entries associated with 'port' and forces ofproto @@ -1230,8 +1442,7 @@ bundle_update(struct ofbundle *bundle) bundle->floodable = true; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { - if (port->up.opp.config & htonl(OFPPC_NO_FLOOD) - || !stp_forward_in_state(port->stp_state)) { + if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) { bundle->floodable = false; break; } @@ -1278,8 +1489,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port, port->bundle = bundle; list_push_back(&bundle->ports, &port->bundle_node); - if (port->up.opp.config & htonl(OFPPC_NO_FLOOD) - || !stp_forward_in_state(port->stp_state)) { + if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) { bundle->floodable = false; } } @@ -1550,8 +1760,7 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size) pdu_size); memcpy(packet_pdu, pdu, pdu_size); - send_packet(ofproto_dpif_cast(port->up.ofproto), port->odp_port, - &packet); + send_packet(port, &packet); ofpbuf_uninit(&packet); } else { VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " @@ -1577,8 +1786,7 @@ bundle_send_learning_packets(struct ofbundle *bundle) learning_packet = bond_compose_learning_packet(bundle->bond, e->mac, e->vlan, (void **)&port); - ret = send_packet(ofproto_dpif_cast(port->up.ofproto), - port->odp_port, learning_packet); + ret = send_packet(port, learning_packet); ofpbuf_delete(learning_packet); if (ret) { error = ret; @@ -1837,6 +2045,24 @@ mirror_destroy(struct ofmirror *mirror) mirror_update_dups(ofproto); } +static int +mirror_get_stats(struct ofproto *ofproto_, void *aux, + uint64_t *packets, uint64_t *bytes) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct ofmirror *mirror = mirror_lookup(ofproto, aux); + + if (!mirror) { + *packets = *bytes = UINT64_MAX; + return 0; + } + + *packets = mirror->packet_count; + *bytes = mirror->byte_count; + + return 0; +} + static int set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans) { @@ -1901,8 +2127,7 @@ port_run(struct ofport_dpif *ofport) ofpbuf_init(&packet, 0); cfm_compose_ccm(ofport->cfm, &packet, ofport->up.opp.hw_addr); - send_packet(ofproto_dpif_cast(ofport->up.ofproto), - ofport->odp_port, &packet); + send_packet(ofport, &packet); ofpbuf_uninit(&packet); } @@ -2065,14 +2290,16 @@ port_is_lacp_current(const struct ofport *ofport_) struct flow_miss { struct hmap_node hmap_node; struct flow flow; + enum odp_key_fitness key_fitness; const struct nlattr *key; size_t key_len; + ovs_be16 initial_tci; struct list packets; }; struct flow_miss_op { union dpif_op dpif_op; - struct facet *facet; + struct subfacet *subfacet; }; /* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each @@ -2155,7 +2382,9 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, static struct flow_miss * flow_miss_create(struct hmap *todo, const struct flow *flow, - const struct nlattr *key, size_t key_len) + enum odp_key_fitness key_fitness, + const struct nlattr *key, size_t key_len, + ovs_be16 initial_tci) { uint32_t hash = flow_hash(flow, 0); struct flow_miss *miss; @@ -2169,8 +2398,10 @@ flow_miss_create(struct hmap *todo, const struct flow *flow, miss = xmalloc(sizeof *miss); hmap_insert(todo, &miss->hmap_node, hash); miss->flow = *flow; + miss->key_fitness = key_fitness; miss->key = key; miss->key_len = key_len; + miss->initial_tci = initial_tci; list_init(&miss->packets); return miss; } @@ -2181,6 +2412,7 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, { const struct flow *flow = &miss->flow; struct ofpbuf *packet, *next_packet; + struct subfacet *subfacet; struct facet *facet; facet = facet_lookup_valid(ofproto, flow); @@ -2214,6 +2446,10 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, facet = facet_create(rule, flow); } + subfacet = subfacet_create(ofproto, facet, + miss->key_fitness, miss->key, miss->key_len, + miss->initial_tci); + LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) { list_remove(&packet->list_node); ofproto->n_matches++; @@ -2232,43 +2468,74 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, send_packet_in_miss(ofproto, packet, flow, true); } - if (!facet->may_install) { - facet_make_actions(ofproto, facet, packet); + if (!facet->may_install || !subfacet->actions) { + subfacet_make_actions(ofproto, subfacet, packet); } if (!execute_controller_action(ofproto, &facet->flow, - facet->actions, facet->actions_len, - packet)) { + subfacet->actions, + subfacet->actions_len, packet)) { struct flow_miss_op *op = &ops[(*n_ops)++]; struct dpif_execute *execute = &op->dpif_op.execute; - op->facet = facet; + op->subfacet = subfacet; execute->type = DPIF_OP_EXECUTE; execute->key = miss->key; execute->key_len = miss->key_len; execute->actions = (facet->may_install - ? facet->actions - : xmemdup(facet->actions, facet->actions_len)); - execute->actions_len = facet->actions_len; + ? subfacet->actions + : xmemdup(subfacet->actions, subfacet->actions_len)); + execute->actions_len = subfacet->actions_len; execute->packet = packet; } } - if (facet->may_install) { + if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) { struct flow_miss_op *op = &ops[(*n_ops)++]; struct dpif_flow_put *put = &op->dpif_op.flow_put; - op->facet = facet; + op->subfacet = subfacet; put->type = DPIF_OP_FLOW_PUT; put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; put->key = miss->key; put->key_len = miss->key_len; - put->actions = facet->actions; - put->actions_len = facet->actions_len; + put->actions = subfacet->actions; + put->actions_len = subfacet->actions_len; put->stats = NULL; } } +static enum odp_key_fitness +ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto, + const struct nlattr *key, size_t key_len, + struct flow *flow, ovs_be16 *initial_tci) +{ + enum odp_key_fitness fitness; + uint16_t realdev; + int vid; + + fitness = odp_flow_key_to_flow(key, key_len, flow); + if (fitness == ODP_FIT_ERROR) { + return fitness; + } + *initial_tci = flow->vlan_tci; + + realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid); + if (realdev) { + /* Cause the flow to be processed as if it came in on the real device + * with the VLAN device's VLAN ID. */ + flow->in_port = realdev; + flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI); + + /* Let the caller know that we can't reproduce 'key' from 'flow'. */ + if (fitness == ODP_FIT_PERFECT) { + fitness = ODP_FIT_TOO_MUCH; + } + } + + return fitness; +} + static void handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, size_t n_upcalls) @@ -2292,12 +2559,19 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, * that we can process them together. */ hmap_init(&todo); for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) { + enum odp_key_fitness fitness; struct flow_miss *miss; + ovs_be16 initial_tci; struct flow flow; - /* Obtain in_port and tun_id, at least, then set 'flow''s header - * pointers. */ - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + /* Obtain metadata and check userspace/kernel agreement on flow match, + * then set 'flow''s header pointers. */ + fitness = ofproto_dpif_extract_flow_key(ofproto, + upcall->key, upcall->key_len, + &flow, &initial_tci); + if (fitness == ODP_FIT_ERROR) { + continue; + } flow_extract(upcall->packet, flow.priority, flow.tun_id, flow.in_port, &flow); @@ -2309,7 +2583,8 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, } /* Add other packets to a to-do list. */ - miss = flow_miss_create(&todo, &flow, upcall->key, upcall->key_len); + miss = flow_miss_create(&todo, &flow, fitness, + upcall->key, upcall->key_len, initial_tci); list_push_back(&miss->packets, &upcall->packet->list_node); } @@ -2340,7 +2615,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, switch (op->dpif_op.type) { case DPIF_OP_EXECUTE: execute = &op->dpif_op.execute; - if (op->facet->actions != execute->actions) { + if (op->subfacet->actions != execute->actions) { free((struct nlattr *) execute->actions); } ofpbuf_delete((struct ofpbuf *) execute->packet); @@ -2349,7 +2624,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, case DPIF_OP_FLOW_PUT: put = &op->dpif_op.flow_put; if (!put->error) { - op->facet->installed = true; + op->subfacet->installed = true; } break; } @@ -2360,21 +2635,28 @@ static void handle_userspace_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) { - struct flow flow; struct user_action_cookie cookie; + enum odp_key_fitness fitness; + ovs_be16 initial_tci; + struct flow flow; memcpy(&cookie, &upcall->userdata, sizeof(cookie)); + fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key, + upcall->key_len, &flow, + &initial_tci); + if (fitness == ODP_FIT_ERROR) { + return; + } + if (cookie.type == USER_ACTION_COOKIE_SFLOW) { if (ofproto->sflow) { - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie); + dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, + &cookie); } ofpbuf_delete(upcall->packet); - } else if (cookie.type == USER_ACTION_COOKIE_CONTROLLER) { COVERAGE_INC(ofproto_dpif_ctlr_action); - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); send_packet_in_action(ofproto, upcall->packet, upcall->userdata, &flow, false); } else { @@ -2403,10 +2685,10 @@ handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) /* Flow expiration. */ -static int facet_max_idle(const struct ofproto_dpif *); +static int subfacet_max_idle(const struct ofproto_dpif *); static void update_stats(struct ofproto_dpif *); static void rule_expire(struct rule_dpif *); -static void expire_facets(struct ofproto_dpif *, int dp_max_idle); +static void expire_subfacets(struct ofproto_dpif *, int dp_max_idle); /* This function is called periodically by run(). Its job is to collect * updates for the flows that have been installed into the datapath, most @@ -2424,9 +2706,9 @@ expire(struct ofproto_dpif *ofproto) /* Update stats for each flow in the datapath. */ update_stats(ofproto); - /* Expire facets that have been idle too long. */ - dp_max_idle = facet_max_idle(ofproto); - expire_facets(ofproto, dp_max_idle); + /* Expire subfacets that have been idle too long. */ + dp_max_idle = subfacet_max_idle(ofproto); + expire_subfacets(ofproto, dp_max_idle); /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */ OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { @@ -2474,46 +2756,41 @@ update_stats(struct ofproto_dpif *p) dpif_flow_dump_start(&dump, p->dpif); while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) { - struct facet *facet; + enum odp_key_fitness fitness; + struct subfacet *subfacet; struct flow flow; - if (odp_flow_key_to_flow(key, key_len, &flow)) { - struct ds s; - - ds_init(&s); - odp_flow_key_format(key, key_len, &s); - VLOG_WARN_RL(&rl, "failed to convert datapath flow key to flow: %s", - ds_cstr(&s)); - ds_destroy(&s); - + fitness = odp_flow_key_to_flow(key, key_len, &flow); + if (fitness == ODP_FIT_ERROR) { continue; } - facet = facet_find(p, &flow); - if (facet && facet->installed) { + subfacet = subfacet_find(p, key, key_len, &flow); + if (subfacet && subfacet->installed) { + struct facet *facet = subfacet->facet; - if (stats->n_packets >= facet->dp_packet_count) { - uint64_t extra = stats->n_packets - facet->dp_packet_count; + if (stats->n_packets >= subfacet->dp_packet_count) { + uint64_t extra = stats->n_packets - subfacet->dp_packet_count; facet->packet_count += extra; } else { VLOG_WARN_RL(&rl, "unexpected packet count from the datapath"); } - if (stats->n_bytes >= facet->dp_byte_count) { - facet->byte_count += stats->n_bytes - facet->dp_byte_count; + if (stats->n_bytes >= subfacet->dp_byte_count) { + facet->byte_count += stats->n_bytes - subfacet->dp_byte_count; } else { VLOG_WARN_RL(&rl, "unexpected byte count from datapath"); } - facet->dp_packet_count = stats->n_packets; - facet->dp_byte_count = stats->n_bytes; + subfacet->dp_packet_count = stats->n_packets; + subfacet->dp_byte_count = stats->n_bytes; - facet_update_time(p, facet, stats->used); + subfacet_update_time(p, subfacet, stats->used); facet_account(p, facet); facet_push_stats(facet); } else { - /* There's a flow in the datapath that we know nothing about. - * Delete it. */ + /* There's a flow in the datapath that we know nothing about, or a + * flow that shouldn't be installed but was anyway. Delete it. */ COVERAGE_INC(facet_unexpected); dpif_flow_del(p->dpif, key, key_len, NULL); } @@ -2522,58 +2799,60 @@ update_stats(struct ofproto_dpif *p) } /* Calculates and returns the number of milliseconds of idle time after which - * facets should expire from the datapath and we should fold their statistics - * into their parent rules in userspace. */ + * subfacets should expire from the datapath. When a subfacet expires, we fold + * its statistics into its facet, and when a facet's last subfacet expires, we + * fold its statistic into its rule. */ static int -facet_max_idle(const struct ofproto_dpif *ofproto) +subfacet_max_idle(const struct ofproto_dpif *ofproto) { /* * Idle time histogram. * - * Most of the time a switch has a relatively small number of facets. When - * this is the case we might as well keep statistics for all of them in - * userspace and to cache them in the kernel datapath for performance as + * Most of the time a switch has a relatively small number of subfacets. + * When this is the case we might as well keep statistics for all of them + * in userspace and to cache them in the kernel datapath for performance as * well. * - * As the number of facets increases, the memory required to maintain + * As the number of subfacets increases, the memory required to maintain * statistics about them in userspace and in the kernel becomes - * significant. However, with a large number of facets it is likely that - * only a few of them are "heavy hitters" that consume a large amount of - * bandwidth. At this point, only heavy hitters are worth caching in the - * kernel and maintaining in userspaces; other facets we can discard. + * significant. However, with a large number of subfacets it is likely + * that only a few of them are "heavy hitters" that consume a large amount + * of bandwidth. At this point, only heavy hitters are worth caching in + * the kernel and maintaining in userspaces; other subfacets we can + * discard. * * The technique used to compute the idle time is to build a histogram with - * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet + * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each subfacet * that is installed in the kernel gets dropped in the appropriate bucket. * After the histogram has been built, we compute the cutoff so that only - * the most-recently-used 1% of facets (but at least + * the most-recently-used 1% of subfacets (but at least * ofproto->up.flow_eviction_threshold flows) are kept cached. At least - * the most-recently-used bucket of facets is kept, so actually an - * arbitrary number of facets can be kept in any given expiration run + * the most-recently-used bucket of subfacets is kept, so actually an + * arbitrary number of subfacets can be kept in any given expiration run * (though the next run will delete most of those unless they receive * additional data). * - * This requires a second pass through the facets, in addition to the pass - * made by update_stats(), because the former function never looks - * at uninstallable facets. + * This requires a second pass through the subfacets, in addition to the + * pass made by update_stats(), because the former function never looks at + * uninstallable subfacets. */ enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) }; enum { N_BUCKETS = 5000 / BUCKET_WIDTH }; int buckets[N_BUCKETS] = { 0 }; int total, subtotal, bucket; - struct facet *facet; + struct subfacet *subfacet; long long int now; int i; - total = hmap_count(&ofproto->facets); + total = hmap_count(&ofproto->subfacets); if (total <= ofproto->up.flow_eviction_threshold) { return N_BUCKETS * BUCKET_WIDTH; } /* Build histogram. */ now = time_msec(); - HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { - long long int idle = now - facet->used; + HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) { + long long int idle = now - subfacet->used; int bucket = (idle <= 0 ? 0 : idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1 : (unsigned int) idle / BUCKET_WIDTH); @@ -2608,38 +2887,15 @@ facet_max_idle(const struct ofproto_dpif *ofproto) } static void -facet_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet) -{ - if (ofproto->netflow && !facet_is_controller_flow(facet) && - netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) { - struct ofexpired expired; - - if (facet->installed) { - struct dpif_flow_stats stats; - - facet_put__(ofproto, facet, facet->actions, facet->actions_len, - &stats); - facet_update_stats(ofproto, facet, &stats); - } - - expired.flow = facet->flow; - expired.packet_count = facet->packet_count; - expired.byte_count = facet->byte_count; - expired.used = facet->used; - netflow_expire(ofproto->netflow, &facet->nf_flow, &expired); - } -} - -static void -expire_facets(struct ofproto_dpif *ofproto, int dp_max_idle) +expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle) { long long int cutoff = time_msec() - dp_max_idle; - struct facet *facet, *next_facet; + struct subfacet *subfacet, *next_subfacet; - HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) { - facet_active_timeout(ofproto, facet); - if (facet->used < cutoff) { - facet_remove(ofproto, facet); + HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node, + &ofproto->subfacets) { + if (subfacet->used < cutoff) { + subfacet_destroy(ofproto, subfacet); } } } @@ -2686,8 +2942,8 @@ rule_expire(struct rule_dpif *rule) * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in * the ofproto's classifier table. * - * The facet will initially have no ODP actions. The caller should fix that - * by calling facet_make_actions(). */ + * The facet will initially have no subfacets. The caller should create (at + * least) one subfacet with subfacet_create(). */ static struct facet * facet_create(struct rule_dpif *rule, const struct flow *flow) { @@ -2700,6 +2956,7 @@ facet_create(struct rule_dpif *rule, const struct flow *flow) list_push_back(&rule->facets, &facet->list_node); facet->rule = rule; facet->flow = *flow; + list_init(&facet->subfacets); netflow_flow_init(&facet->nf_flow); netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used); @@ -2709,7 +2966,6 @@ facet_create(struct rule_dpif *rule, const struct flow *flow) static void facet_free(struct facet *facet) { - free(facet->actions); free(facet); } @@ -2768,131 +3024,34 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, return !error; } -/* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s - * statistics appropriately. 'packet' must have at least sizeof(struct - * ofp_packet_in) bytes of headroom. - * - * For correct results, 'packet' must actually be in 'facet''s flow; that is, - * applying flow_extract() to 'packet' would yield the same flow as - * 'facet->flow'. - * - * 'facet' must have accurately composed datapath actions; that is, it must - * not be in need of revalidation. - * - * Takes ownership of 'packet'. */ -static void -facet_execute(struct ofproto_dpif *ofproto, struct facet *facet, - struct ofpbuf *packet) -{ - struct dpif_flow_stats stats; - - assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in)); - - dpif_flow_stats_extract(&facet->flow, packet, &stats); - stats.used = time_msec(); - if (execute_odp_actions(ofproto, &facet->flow, - facet->actions, facet->actions_len, packet)) { - facet_update_stats(ofproto, facet, &stats); - } -} - /* Remove 'facet' from 'ofproto' and free up the associated memory: * * - If 'facet' was installed in the datapath, uninstalls it and updates its - * rule's statistics, via facet_uninstall(). + * rule's statistics, via subfacet_uninstall(). * * - Removes 'facet' from its rule and from ofproto->facets. */ static void facet_remove(struct ofproto_dpif *ofproto, struct facet *facet) { - facet_uninstall(ofproto, facet); + struct subfacet *subfacet, *next_subfacet; + + LIST_FOR_EACH_SAFE (subfacet, next_subfacet, list_node, + &facet->subfacets) { + subfacet_destroy__(ofproto, subfacet); + } + facet_flush_stats(ofproto, facet); hmap_remove(&ofproto->facets, &facet->hmap_node); list_remove(&facet->list_node); facet_free(facet); } -/* Composes the datapath actions for 'facet' based on its rule's actions. */ -static void -facet_make_actions(struct ofproto_dpif *p, struct facet *facet, - const struct ofpbuf *packet) -{ - const struct rule_dpif *rule = facet->rule; - struct ofpbuf *odp_actions; - struct action_xlate_ctx ctx; - - action_xlate_ctx_init(&ctx, p, &facet->flow, packet); - odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions); - facet->tags = ctx.tags; - facet->may_install = ctx.may_set_up_flow; - facet->has_learn = ctx.has_learn; - facet->has_normal = ctx.has_normal; - facet->nf_flow.output_iface = ctx.nf_output_iface; - - if (facet->actions_len != odp_actions->size - || memcmp(facet->actions, odp_actions->data, odp_actions->size)) { - free(facet->actions); - facet->actions_len = odp_actions->size; - facet->actions = xmemdup(odp_actions->data, odp_actions->size); - } - - ofpbuf_delete(odp_actions); -} - -/* Updates 'facet''s flow in the datapath setting its actions to 'actions_len' - * bytes of actions in 'actions'. If 'stats' is non-null, statistics counters - * in the datapath will be zeroed and 'stats' will be updated with traffic new - * since 'facet' was last updated. - * - * Returns 0 if successful, otherwise a positive errno value.*/ -static int -facet_put__(struct ofproto_dpif *ofproto, struct facet *facet, - const struct nlattr *actions, size_t actions_len, - struct dpif_flow_stats *stats) -{ - struct odputil_keybuf keybuf; - enum dpif_flow_put_flags flags; - struct ofpbuf key; - int ret; - - flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; - if (stats) { - flags |= DPIF_FP_ZERO_STATS; - } - - ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); - odp_flow_key_from_flow(&key, &facet->flow); - - ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size, - actions, actions_len, stats); - - if (stats) { - facet_reset_dp_stats(facet, stats); - } - - return ret; -} - -/* If 'facet' is installable, inserts or re-inserts it into 'p''s datapath. If - * 'zero_stats' is true, clears any existing statistics from the datapath for - * 'facet'. */ -static void -facet_install(struct ofproto_dpif *p, struct facet *facet, bool zero_stats) -{ - struct dpif_flow_stats stats; - - if (facet->may_install - && !facet_put__(p, facet, facet->actions, facet->actions_len, - zero_stats ? &stats : NULL)) { - facet->installed = true; - } -} - static void facet_account(struct ofproto_dpif *ofproto, struct facet *facet) { uint64_t n_bytes; + struct subfacet *subfacet; const struct nlattr *a; unsigned int left; ovs_be16 vlan_tci; @@ -2909,7 +3068,8 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) if (facet->has_learn || facet->has_normal) { struct action_xlate_ctx ctx; - action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL); + action_xlate_ctx_init(&ctx, ofproto, &facet->flow, + facet->flow.vlan_tci, NULL); ctx.may_learn = true; ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions, facet->rule->up.n_actions)); @@ -2923,9 +3083,15 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) * as a basis. We also need to track the actual VLAN on which the packet * is going to be sent to ensure that it matches the one passed to * bond_choose_output_slave(). (Otherwise, we will account to the wrong - * hash bucket.) */ + * hash bucket.) + * + * We use the actions from an arbitrary subfacet because they should all + * be equally valid for our purpose. */ + subfacet = CONTAINER_OF(list_front(&facet->subfacets), + struct subfacet, list_node); vlan_tci = facet->flow.vlan_tci; - NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) { + NL_ATTR_FOR_EACH_UNSAFE (a, left, + subfacet->actions, subfacet->actions_len) { const struct ovs_action_push_vlan *vlan; struct ofport_dpif *port; @@ -2950,31 +3116,6 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) } } -/* If 'rule' is installed in the datapath, uninstalls it. */ -static void -facet_uninstall(struct ofproto_dpif *p, struct facet *facet) -{ - if (facet->installed) { - struct odputil_keybuf keybuf; - struct dpif_flow_stats stats; - struct ofpbuf key; - int error; - - ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); - odp_flow_key_from_flow(&key, &facet->flow); - - error = dpif_flow_del(p->dpif, key.data, key.size, &stats); - facet_reset_dp_stats(facet, &stats); - if (!error) { - facet_update_stats(p, facet, &stats); - } - facet->installed = false; - } else { - assert(facet->dp_packet_count == 0); - assert(facet->dp_byte_count == 0); - } -} - /* Returns true if the only action for 'facet' is to send to the controller. * (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). */ @@ -2987,24 +3128,6 @@ facet_is_controller_flow(struct facet *facet) htons(OFPP_CONTROLLER))); } -/* Resets 'facet''s datapath statistics counters. This should be called when - * 'facet''s statistics are cleared in the datapath. If 'stats' is non-null, - * it should contain the statistics returned by dpif when 'facet' was reset in - * the datapath. 'stats' will be modified to only included statistics new - * since 'facet' was last updated. */ -static void -facet_reset_dp_stats(struct facet *facet, struct dpif_flow_stats *stats) -{ - if (stats && facet->dp_packet_count <= stats->n_packets - && facet->dp_byte_count <= stats->n_bytes) { - stats->n_packets -= facet->dp_packet_count; - stats->n_bytes -= facet->dp_byte_count; - } - - facet->dp_packet_count = 0; - facet->dp_byte_count = 0; -} - /* Folds all of 'facet''s statistics into its rule. Also updates the * accounting ofhook and emits a NetFlow expiration if appropriate. All of * 'facet''s statistics in the datapath should have been zeroed and folded into @@ -3012,8 +3135,12 @@ facet_reset_dp_stats(struct facet *facet, struct dpif_flow_stats *stats) static void facet_flush_stats(struct ofproto_dpif *ofproto, struct facet *facet) { - assert(!facet->dp_byte_count); - assert(!facet->dp_packet_count); + struct subfacet *subfacet; + + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + assert(!subfacet->dp_byte_count); + assert(!subfacet->dp_packet_count); + } facet_push_stats(facet); facet_account(ofproto, facet); @@ -3093,10 +3220,17 @@ facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow) static bool facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) { + struct actions { + struct nlattr *odp_actions; + size_t actions_len; + }; + struct actions *new_actions; + struct action_xlate_ctx ctx; - struct ofpbuf *odp_actions; struct rule_dpif *new_rule; + struct subfacet *subfacet; bool actions_changed; + int i; COVERAGE_INC(facet_revalidate); @@ -3113,28 +3247,50 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) * We do not modify any 'facet' state yet, because we might need to, e.g., * emit a NetFlow expiration and, if so, we need to have the old state * around to properly compose it. */ - action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL); - odp_actions = xlate_actions(&ctx, - new_rule->up.actions, new_rule->up.n_actions); - actions_changed = (facet->actions_len != odp_actions->size - || memcmp(facet->actions, odp_actions->data, - facet->actions_len)); /* If the datapath actions changed or the installability changed, * then we need to talk to the datapath. */ - if (actions_changed || ctx.may_set_up_flow != facet->installed) { - if (ctx.may_set_up_flow) { - struct dpif_flow_stats stats; + i = 0; + new_actions = NULL; + memset(&ctx, 0, sizeof ctx); + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + struct ofpbuf *odp_actions; + bool should_install; + + action_xlate_ctx_init(&ctx, ofproto, &facet->flow, + subfacet->initial_tci, NULL); + odp_actions = xlate_actions(&ctx, new_rule->up.actions, + new_rule->up.n_actions); + actions_changed = (subfacet->actions_len != odp_actions->size + || memcmp(subfacet->actions, odp_actions->data, + subfacet->actions_len)); + + should_install = (ctx.may_set_up_flow + && subfacet->key_fitness != ODP_FIT_TOO_LITTLE); + if (actions_changed || should_install != subfacet->installed) { + if (should_install) { + struct dpif_flow_stats stats; + + subfacet_install(ofproto, subfacet, + odp_actions->data, odp_actions->size, &stats); + subfacet_update_stats(ofproto, subfacet, &stats); + } else { + subfacet_uninstall(ofproto, subfacet); + } - facet_put__(ofproto, facet, - odp_actions->data, odp_actions->size, &stats); - facet_update_stats(ofproto, facet, &stats); - } else { - facet_uninstall(ofproto, facet); + if (!new_actions) { + new_actions = xcalloc(list_size(&facet->subfacets), + sizeof *new_actions); + } + new_actions[i].odp_actions = xmemdup(odp_actions->data, + odp_actions->size); + new_actions[i].actions_len = odp_actions->size; } - /* The datapath flow is gone or has zeroed stats, so push stats out of - * 'facet' into 'rule'. */ + ofpbuf_delete(odp_actions); + i++; + } + if (new_actions) { facet_flush_stats(ofproto, facet); } @@ -3144,10 +3300,18 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) facet->may_install = ctx.may_set_up_flow; facet->has_learn = ctx.has_learn; facet->has_normal = ctx.has_normal; - if (actions_changed) { - free(facet->actions); - facet->actions_len = odp_actions->size; - facet->actions = xmemdup(odp_actions->data, odp_actions->size); + facet->mirrors = ctx.mirrors; + if (new_actions) { + i = 0; + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + if (new_actions[i].odp_actions) { + free(subfacet->actions); + subfacet->actions = new_actions[i].odp_actions; + subfacet->actions_len = new_actions[i].actions_len; + } + i++; + } + free(new_actions); } if (facet->rule != new_rule) { COVERAGE_INC(facet_changed_rule); @@ -3155,11 +3319,9 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) list_push_back(&new_rule->facets, &facet->list_node); facet->rule = new_rule; facet->used = new_rule->up.created; - facet->rs_used = facet->used; + facet->prev_used = facet->used; } - ofpbuf_delete(odp_actions); - return true; } @@ -3178,54 +3340,38 @@ facet_update_time(struct ofproto_dpif *ofproto, struct facet *facet, } } -/* Folds the statistics from 'stats' into the counters in 'facet'. - * - * Because of the meaning of a facet's counters, it only makes sense to do this - * if 'stats' are not tracked in the datapath, that is, if 'stats' represents a - * packet that was sent by hand or if it represents statistics that have been - * cleared out of the datapath. */ -static void -facet_update_stats(struct ofproto_dpif *ofproto, struct facet *facet, - const struct dpif_flow_stats *stats) -{ - if (stats->n_packets || stats->used > facet->used) { - facet_update_time(ofproto, facet, stats->used); - facet->packet_count += stats->n_packets; - facet->byte_count += stats->n_bytes; - facet_push_stats(facet); - netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags); - } -} - static void facet_reset_counters(struct facet *facet) { facet->packet_count = 0; facet->byte_count = 0; - facet->rs_packet_count = 0; - facet->rs_byte_count = 0; + facet->prev_packet_count = 0; + facet->prev_byte_count = 0; facet->accounted_bytes = 0; } static void facet_push_stats(struct facet *facet) { - uint64_t rs_packets, rs_bytes; + uint64_t new_packets, new_bytes; - assert(facet->packet_count >= facet->rs_packet_count); - assert(facet->byte_count >= facet->rs_byte_count); - assert(facet->used >= facet->rs_used); + assert(facet->packet_count >= facet->prev_packet_count); + assert(facet->byte_count >= facet->prev_byte_count); + assert(facet->used >= facet->prev_used); - rs_packets = facet->packet_count - facet->rs_packet_count; - rs_bytes = facet->byte_count - facet->rs_byte_count; + new_packets = facet->packet_count - facet->prev_packet_count; + new_bytes = facet->byte_count - facet->prev_byte_count; - if (rs_packets || rs_bytes || facet->used > facet->rs_used) { - facet->rs_packet_count = facet->packet_count; - facet->rs_byte_count = facet->byte_count; - facet->rs_used = facet->used; + if (new_packets || new_bytes || facet->used > facet->prev_used) { + facet->prev_packet_count = facet->packet_count; + facet->prev_byte_count = facet->byte_count; + facet->prev_used = facet->used; flow_push_stats(facet->rule, &facet->flow, - rs_packets, rs_bytes, facet->used); + new_packets, new_bytes, facet->used); + + update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto), + facet->mirrors, new_packets, new_bytes); } } @@ -3249,10 +3395,10 @@ push_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) } /* Pushes flow statistics to the rules which 'flow' resubmits into given - * 'rule''s actions. */ + * 'rule''s actions and mirrors. */ static void flow_push_stats(const struct rule_dpif *rule, - struct flow *flow, uint64_t packets, uint64_t bytes, + const struct flow *flow, uint64_t packets, uint64_t bytes, long long int used) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); @@ -3262,12 +3408,267 @@ flow_push_stats(const struct rule_dpif *rule, push.bytes = bytes; push.used = used; - action_xlate_ctx_init(&push.ctx, ofproto, flow, NULL); + action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL); push.ctx.resubmit_hook = push_resubmit; ofpbuf_delete(xlate_actions(&push.ctx, rule->up.actions, rule->up.n_actions)); } +/* Subfacets. */ + +static struct subfacet * +subfacet_find__(struct ofproto_dpif *ofproto, + const struct nlattr *key, size_t key_len, uint32_t key_hash, + const struct flow *flow) +{ + struct subfacet *subfacet; + + HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash, + &ofproto->subfacets) { + if (subfacet->key + ? (subfacet->key_len == key_len + && !memcmp(key, subfacet->key, key_len)) + : flow_equal(flow, &subfacet->facet->flow)) { + return subfacet; + } + } + + return NULL; +} + +/* Searches 'facet' (within 'ofproto') for a subfacet with the specified + * 'key_fitness', 'key', and 'key_len'. Returns the existing subfacet if + * there is one, otherwise creates and returns a new subfacet. + * + * If the returned subfacet is new, then subfacet->actions will be NULL, in + * which case the caller must populate the actions with + * subfacet_make_actions(). */ +static struct subfacet * +subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, + enum odp_key_fitness key_fitness, + const struct nlattr *key, size_t key_len, ovs_be16 initial_tci) +{ + uint32_t key_hash = odp_flow_key_hash(key, key_len); + struct subfacet *subfacet; + + subfacet = subfacet_find__(ofproto, key, key_len, key_hash, &facet->flow); + if (subfacet) { + if (subfacet->facet == facet) { + return subfacet; + } + + /* This shouldn't happen. */ + VLOG_ERR_RL(&rl, "subfacet with wrong facet"); + subfacet_destroy(ofproto, subfacet); + } + + subfacet = xzalloc(sizeof *subfacet); + hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash); + list_push_back(&facet->subfacets, &subfacet->list_node); + subfacet->facet = facet; + subfacet->used = time_msec(); + subfacet->key_fitness = key_fitness; + if (key_fitness != ODP_FIT_PERFECT) { + subfacet->key = xmemdup(key, key_len); + subfacet->key_len = key_len; + } + subfacet->installed = false; + subfacet->initial_tci = initial_tci; + + return subfacet; +} + +/* Searches 'ofproto' for a subfacet with the given 'key', 'key_len', and + * 'flow'. Returns the subfacet if one exists, otherwise NULL. */ +static struct subfacet * +subfacet_find(struct ofproto_dpif *ofproto, + const struct nlattr *key, size_t key_len, + const struct flow *flow) +{ + uint32_t key_hash = odp_flow_key_hash(key, key_len); + + return subfacet_find__(ofproto, key, key_len, key_hash, flow); +} + +/* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from + * its facet within 'ofproto', and frees it. */ +static void +subfacet_destroy__(struct ofproto_dpif *ofproto, struct subfacet *subfacet) +{ + subfacet_uninstall(ofproto, subfacet); + hmap_remove(&ofproto->subfacets, &subfacet->hmap_node); + list_remove(&subfacet->list_node); + free(subfacet->key); + free(subfacet->actions); + free(subfacet); +} + +/* Destroys 'subfacet', as with subfacet_destroy__(), and then if this was the + * last remaining subfacet in its facet destroys the facet too. */ +static void +subfacet_destroy(struct ofproto_dpif *ofproto, struct subfacet *subfacet) +{ + struct facet *facet = subfacet->facet; + + subfacet_destroy__(ofproto, subfacet); + if (list_is_empty(&facet->subfacets)) { + facet_remove(ofproto, facet); + } +} + +/* Initializes 'key' with the sequence of OVS_KEY_ATTR_* Netlink attributes + * that can be used to refer to 'subfacet'. The caller must provide 'keybuf' + * for use as temporary storage. */ +static void +subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf, + struct ofpbuf *key) +{ + if (!subfacet->key) { + ofpbuf_use_stack(key, keybuf, sizeof *keybuf); + odp_flow_key_from_flow(key, &subfacet->facet->flow); + } else { + ofpbuf_use_const(key, subfacet->key, subfacet->key_len); + } +} + +/* Composes the datapath actions for 'subfacet' based on its rule's actions. */ +static void +subfacet_make_actions(struct ofproto_dpif *p, struct subfacet *subfacet, + const struct ofpbuf *packet) +{ + struct facet *facet = subfacet->facet; + const struct rule_dpif *rule = facet->rule; + struct ofpbuf *odp_actions; + struct action_xlate_ctx ctx; + + action_xlate_ctx_init(&ctx, p, &facet->flow, subfacet->initial_tci, + packet); + odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions); + facet->tags = ctx.tags; + facet->may_install = ctx.may_set_up_flow; + facet->has_learn = ctx.has_learn; + facet->has_normal = ctx.has_normal; + facet->nf_flow.output_iface = ctx.nf_output_iface; + facet->mirrors = ctx.mirrors; + + if (subfacet->actions_len != odp_actions->size + || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) { + free(subfacet->actions); + subfacet->actions_len = odp_actions->size; + subfacet->actions = xmemdup(odp_actions->data, odp_actions->size); + } + + ofpbuf_delete(odp_actions); +} + +/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len' + * bytes of actions in 'actions'. If 'stats' is non-null, statistics counters + * in the datapath will be zeroed and 'stats' will be updated with traffic new + * since 'subfacet' was last updated. + * + * Returns 0 if successful, otherwise a positive errno value. */ +static int +subfacet_install(struct ofproto_dpif *ofproto, struct subfacet *subfacet, + const struct nlattr *actions, size_t actions_len, + struct dpif_flow_stats *stats) +{ + struct odputil_keybuf keybuf; + enum dpif_flow_put_flags flags; + struct ofpbuf key; + int ret; + + flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; + if (stats) { + flags |= DPIF_FP_ZERO_STATS; + } + + subfacet_get_key(subfacet, &keybuf, &key); + ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size, + actions, actions_len, stats); + + if (stats) { + subfacet_reset_dp_stats(subfacet, stats); + } + + return ret; +} + +/* If 'subfacet' is installed in the datapath, uninstalls it. */ +static void +subfacet_uninstall(struct ofproto_dpif *p, struct subfacet *subfacet) +{ + if (subfacet->installed) { + struct odputil_keybuf keybuf; + struct dpif_flow_stats stats; + struct ofpbuf key; + int error; + + subfacet_get_key(subfacet, &keybuf, &key); + error = dpif_flow_del(p->dpif, key.data, key.size, &stats); + subfacet_reset_dp_stats(subfacet, &stats); + if (!error) { + subfacet_update_stats(p, subfacet, &stats); + } + subfacet->installed = false; + } else { + assert(subfacet->dp_packet_count == 0); + assert(subfacet->dp_byte_count == 0); + } +} + +/* Resets 'subfacet''s datapath statistics counters. This should be called + * when 'subfacet''s statistics are cleared in the datapath. If 'stats' is + * non-null, it should contain the statistics returned by dpif when 'subfacet' + * was reset in the datapath. 'stats' will be modified to include only + * statistics new since 'subfacet' was last updated. */ +static void +subfacet_reset_dp_stats(struct subfacet *subfacet, + struct dpif_flow_stats *stats) +{ + if (stats + && subfacet->dp_packet_count <= stats->n_packets + && subfacet->dp_byte_count <= stats->n_bytes) { + stats->n_packets -= subfacet->dp_packet_count; + stats->n_bytes -= subfacet->dp_byte_count; + } + + subfacet->dp_packet_count = 0; + subfacet->dp_byte_count = 0; +} + +/* Updates 'subfacet''s used time. The caller is responsible for calling + * facet_push_stats() to update the flows which 'subfacet' resubmits into. */ +static void +subfacet_update_time(struct ofproto_dpif *ofproto, struct subfacet *subfacet, + long long int used) +{ + if (used > subfacet->used) { + subfacet->used = used; + facet_update_time(ofproto, subfacet->facet, used); + } +} + +/* Folds the statistics from 'stats' into the counters in 'subfacet'. + * + * Because of the meaning of a subfacet's counters, it only makes sense to do + * this if 'stats' are not tracked in the datapath, that is, if 'stats' + * represents a packet that was sent by hand or if it represents statistics + * that have been cleared out of the datapath. */ +static void +subfacet_update_stats(struct ofproto_dpif *ofproto, struct subfacet *subfacet, + const struct dpif_flow_stats *stats) +{ + if (stats->n_packets || stats->used > subfacet->used) { + struct facet *facet = subfacet->facet; + + subfacet_update_time(ofproto, subfacet, stats->used); + facet->packet_count += stats->n_packets; + facet->byte_count += stats->n_bytes; + facet_push_stats(facet); + netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags); + } +} + /* Rules. */ static struct rule_dpif * @@ -3410,38 +3811,16 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes) } static int -rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet) +rule_execute(struct rule *rule_, const struct flow *flow, + struct ofpbuf *packet) { struct rule_dpif *rule = rule_dpif_cast(rule_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); struct action_xlate_ctx ctx; struct ofpbuf *odp_actions; - struct facet *facet; size_t size; - /* First look for a related facet. If we find one, account it to that. */ - facet = facet_lookup_valid(ofproto, flow); - if (facet && facet->rule == rule) { - if (!facet->may_install) { - facet_make_actions(ofproto, facet, packet); - } - facet_execute(ofproto, facet, packet); - return 0; - } - - /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then - * create a new facet for it and use that. */ - if (rule_dpif_lookup(ofproto, flow, 0) == rule) { - facet = facet_create(rule, flow); - facet_make_actions(ofproto, facet, packet); - facet_execute(ofproto, facet, packet); - facet_install(ofproto, facet, true); - return 0; - } - - /* We can't account anything to a facet. If we were to try, then that - * facet would have a non-matching rule, busting our invariants. */ - action_xlate_ctx_init(&ctx, ofproto, flow, packet); + action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, packet); odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions); size = packet->size; if (execute_odp_actions(ofproto, flow, odp_actions->data, @@ -3473,18 +3852,27 @@ rule_modify_actions(struct rule *rule_) complete_operation(rule); } -/* Sends 'packet' out of port 'odp_port' within 'ofproto'. +/* Sends 'packet' out 'ofport'. + * May modify 'packet'. * Returns 0 if successful, otherwise a positive errno value. */ static int -send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, - const struct ofpbuf *packet) +send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) { + const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct ofpbuf key, odp_actions; struct odputil_keybuf keybuf; + uint16_t odp_port; struct flow flow; int error; flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow); + odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port, + flow.vlan_tci); + if (odp_port != ofport->odp_port) { + eth_pop_vlan(packet); + flow.vlan_tci = htons(0); + } + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, &flow); @@ -3656,27 +4044,26 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base, } static void -commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci) +commit_vlan_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) { - struct flow *base = &ctx->base_flow; - - if (base->vlan_tci == new_tci) { + if (base->vlan_tci == flow->vlan_tci) { return; } if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN); + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); } - if (new_tci & htons(VLAN_CFI)) { + if (flow->vlan_tci & htons(VLAN_CFI)) { struct ovs_action_push_vlan vlan; vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = new_tci; - nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, + vlan.vlan_tci = flow->vlan_tci; + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, &vlan, sizeof vlan); } - base->vlan_tci = new_tci; + base->vlan_tci = flow->vlan_tci; } static void @@ -3700,9 +4087,9 @@ commit_set_nw_action(const struct flow *flow, struct flow *base, ipv4_key.ipv4_src = base->nw_src = flow->nw_src; ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; + ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos; + ipv4_key.ipv4_ttl = base->nw_ttl = flow->nw_ttl; ipv4_key.ipv4_proto = base->nw_proto; - ipv4_key.ipv4_tos = flow->nw_tos; - ipv4_key.ipv4_ttl = flow->nw_ttl; ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE : base->nw_frag == FLOW_NW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); @@ -3766,43 +4153,60 @@ commit_odp_actions(struct action_xlate_ctx *ctx) commit_set_tun_id_action(flow, base, odp_actions); commit_set_ether_addr_action(flow, base, odp_actions); - commit_vlan_action(ctx, flow->vlan_tci); + commit_vlan_action(flow, base, odp_actions); commit_set_nw_action(flow, base, odp_actions); commit_set_port_action(flow, base, odp_actions); commit_set_priority_action(flow, base, odp_actions); } static void -compose_output_action(struct action_xlate_ctx *ctx, uint16_t odp_port) -{ - nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); - ctx->sflow_odp_port = odp_port; - ctx->sflow_n_outputs++; -} - -static void -add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) +compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, + bool check_stp) { const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port); uint16_t odp_port = ofp_port_to_odp_port(ofp_port); + ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci; + uint8_t flow_nw_tos = ctx->flow.nw_tos; + uint16_t out_port; if (ofport) { + struct priority_to_dscp *pdscp; + if (ofport->up.opp.config & htonl(OFPPC_NO_FWD) - || !stp_forward_in_state(ofport->stp_state)) { - /* Forwarding disabled on port. */ + || (check_stp && !stp_forward_in_state(ofport->stp_state))) { return; } + + pdscp = get_priority(ofport, ctx->flow.priority); + if (pdscp) { + ctx->flow.nw_tos &= ~IP_DSCP_MASK; + ctx->flow.nw_tos |= pdscp->dscp; + } } else { - /* - * We don't have an ofport record for this port, but it doesn't hurt to - * allow forwarding to it anyhow. Maybe such a port will appear later - * and we're pre-populating the flow table. - */ + /* We may not have an ofport record for this port, but it doesn't hurt + * to allow forwarding to it anyhow. Maybe such a port will appear + * later and we're pre-populating the flow table. */ } + out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port, + ctx->flow.vlan_tci); + if (out_port != odp_port) { + ctx->flow.vlan_tci = htons(0); + } commit_odp_actions(ctx); - compose_output_action(ctx, odp_port); + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port); + + ctx->sflow_odp_port = odp_port; + ctx->sflow_n_outputs++; ctx->nf_output_iface = ofp_port; + ctx->flow.vlan_tci = flow_vlan_tci; + ctx->flow.nw_tos = flow_nw_tos; +} + +static void +compose_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) +{ + compose_output_action__(ctx, ofp_port, true); } static void @@ -3874,17 +4278,22 @@ xlate_resubmit_table(struct action_xlate_ctx *ctx, } static void -flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask) +flood_packets(struct action_xlate_ctx *ctx, bool all) { struct ofport_dpif *ofport; commit_odp_actions(ctx); HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) { uint16_t ofp_port = ofport->up.ofp_port; - if (ofp_port != ctx->flow.in_port - && !(ofport->up.opp.config & mask) - && stp_forward_in_state(ofport->stp_state)) { - compose_output_action(ctx, ofport->odp_port); + + if (ofp_port == ctx->flow.in_port) { + continue; + } + + if (all) { + compose_output_action__(ctx, ofp_port, false); + } else if (!(ofport->up.opp.config & htonl(OFPPC_NO_FLOOD))) { + compose_output_action(ctx, ofp_port); } } @@ -3896,6 +4305,7 @@ compose_controller_action(struct action_xlate_ctx *ctx, int len) { struct user_action_cookie cookie; + commit_odp_actions(ctx); cookie.type = USER_ACTION_COOKIE_CONTROLLER; cookie.data = len; cookie.n_output = 0; @@ -3913,7 +4323,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, switch (port) { case OFPP_IN_PORT: - add_output_action(ctx, ctx->flow.in_port); + compose_output_action(ctx, ctx->flow.in_port); break; case OFPP_TABLE: xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id); @@ -3922,23 +4332,22 @@ xlate_output_action__(struct action_xlate_ctx *ctx, xlate_normal(ctx); break; case OFPP_FLOOD: - flood_packets(ctx, htonl(OFPPC_NO_FLOOD)); + flood_packets(ctx, false); break; case OFPP_ALL: - flood_packets(ctx, htonl(0)); + flood_packets(ctx, true); break; case OFPP_CONTROLLER: - commit_odp_actions(ctx); compose_controller_action(ctx, max_len); break; case OFPP_LOCAL: - add_output_action(ctx, OFPP_LOCAL); + compose_output_action(ctx, OFPP_LOCAL); break; case OFPP_NONE: break; default: if (port != ctx->flow.in_port) { - add_output_action(ctx, port); + compose_output_action(ctx, port); } break; } @@ -3977,7 +4386,7 @@ static void xlate_enqueue_action(struct action_xlate_ctx *ctx, const struct ofp_action_enqueue *oae) { - uint16_t ofp_port, odp_port; + uint16_t ofp_port; uint32_t flow_priority, priority; int error; @@ -3996,12 +4405,11 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, } else if (ofp_port == ctx->flow.in_port) { return; } - odp_port = ofp_port_to_odp_port(ofp_port); /* Add datapath actions. */ flow_priority = ctx->flow.priority; ctx->flow.priority = priority; - add_output_action(ctx, odp_port); + compose_output_action(ctx, ofp_port); ctx->flow.priority = flow_priority; /* Update NetFlow output port. */ @@ -4301,10 +4709,13 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, static void action_xlate_ctx_init(struct action_xlate_ctx *ctx, struct ofproto_dpif *ofproto, const struct flow *flow, - const struct ofpbuf *packet) + ovs_be16 initial_tci, const struct ofpbuf *packet) { ctx->ofproto = ofproto; ctx->flow = *flow; + ctx->base_flow = ctx->flow; + ctx->base_flow.tun_id = 0; + ctx->base_flow.vlan_tci = initial_tci; ctx->packet = packet; ctx->may_learn = packet != NULL; ctx->resubmit_hook = NULL; @@ -4314,6 +4725,8 @@ static struct ofpbuf * xlate_actions(struct action_xlate_ctx *ctx, const union ofp_action *in, size_t n_in) { + struct flow orig_flow = ctx->flow; + COVERAGE_INC(ofproto_dpif_xlate); ctx->odp_actions = ofpbuf_new(512); @@ -4323,10 +4736,9 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->has_learn = false; ctx->has_normal = false; ctx->nf_output_iface = NF_OUT_DROP; + ctx->mirrors = 0; ctx->recurse = 0; ctx->original_priority = ctx->flow.priority; - ctx->base_flow = ctx->flow; - ctx->base_flow.tun_id = 0; ctx->table_id = 0; ctx->exit = false; @@ -4364,9 +4776,10 @@ xlate_actions(struct action_xlate_ctx *ctx, if (ctx->packet && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow, ctx->packet)) { - compose_output_action(ctx, OVSP_LOCAL); + compose_output_action(ctx, OFPP_LOCAL); } } + add_mirror_actions(ctx, &orig_flow); fix_sflow_action(ctx); } @@ -4487,7 +4900,7 @@ output_normal(struct action_xlate_ctx *ctx, const struct ofbundle *out_bundle, { struct ofport_dpif *port; uint16_t vid; - ovs_be16 tci; + ovs_be16 tci, old_tci; vid = output_vlan_to_vid(out_bundle, vlan); if (!out_bundle->bond) { @@ -4501,6 +4914,7 @@ output_normal(struct action_xlate_ctx *ctx, const struct ofbundle *out_bundle, } } + old_tci = ctx->flow.vlan_tci; tci = htons(vid); if (tci || out_bundle->use_priority_tags) { tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK); @@ -4508,10 +4922,10 @@ output_normal(struct action_xlate_ctx *ctx, const struct ofbundle *out_bundle, tci |= htons(VLAN_CFI); } } - commit_vlan_action(ctx, tci); + ctx->flow.vlan_tci = tci; - compose_output_action(ctx, port->odp_port); - ctx->nf_output_iface = port->up.ofp_port; + compose_output_action(ctx, port->up.ofp_port); + ctx->flow.vlan_tci = old_tci; } static int @@ -4542,34 +4956,6 @@ ofbundle_get_a_port(const struct ofbundle *bundle) struct ofport_dpif, bundle_node); } -static mirror_mask_t -compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan, - const struct ofbundle *in_bundle, - const struct ofbundle *out_bundle) -{ - mirror_mask_t dst_mirrors = 0; - - if (out_bundle == OFBUNDLE_FLOOD) { - struct ofbundle *bundle; - - HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) { - if (bundle != in_bundle - && ofbundle_includes_vlan(bundle, vlan) - && bundle->floodable - && !bundle->mirror_out) { - output_normal(ctx, bundle, vlan); - dst_mirrors |= bundle->dst_mirrors; - } - } - ctx->nf_output_iface = NF_OUT_FLOOD; - } else if (out_bundle) { - output_normal(ctx, out_bundle, vlan); - dst_mirrors = out_bundle->dst_mirrors; - } - - return dst_mirrors; -} - static bool vlan_is_mirrored(const struct ofmirror *m, int vlan) { @@ -4618,18 +5004,68 @@ eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN]) } static void -output_mirrors(struct action_xlate_ctx *ctx, - uint16_t vlan, const struct ofbundle *in_bundle, - mirror_mask_t dst_mirrors) +add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow) { struct ofproto_dpif *ofproto = ctx->ofproto; mirror_mask_t mirrors; + struct ofport_dpif *in_port; + struct ofbundle *in_bundle; + uint16_t vlan; + uint16_t vid; + const struct nlattr *a; + size_t left; + + /* Obtain in_port from orig_flow.in_port. + * + * lookup_input_bundle() also ensures that in_port belongs to a bundle. */ + in_port = lookup_input_bundle(ctx->ofproto, orig_flow->in_port, + ctx->packet != NULL); + if (!in_port) { + return; + } + in_bundle = in_port->bundle; + mirrors = in_bundle->src_mirrors; + + /* Drop frames on bundles reserved for mirroring. */ + if (in_bundle->mirror_out) { + if (ctx->packet != NULL) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port " + "%s, which is reserved exclusively for mirroring", + ctx->ofproto->up.name, in_bundle->name); + } + return; + } + + /* Check VLAN. */ + vid = vlan_tci_to_vid(orig_flow->vlan_tci); + if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) { + return; + } + vlan = input_vid_to_vlan(in_bundle, vid); + + /* Look at the output ports to check for destination selections. */ + + NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data, + ctx->odp_actions->size) { + enum ovs_action_attr type = nl_attr_type(a); + struct ofport_dpif *ofport; + + if (type != OVS_ACTION_ATTR_OUTPUT) { + continue; + } + + ofport = get_odp_port(ofproto, nl_attr_get_u32(a)); + mirrors |= ofport ? ofport->bundle->dst_mirrors : 0; + } - mirrors = in_bundle->src_mirrors | dst_mirrors; if (!mirrors) { return; } + /* Restore the original packet before adding the mirror actions. */ + ctx->flow = *orig_flow; + while (mirrors) { struct ofmirror *m; @@ -4641,9 +5077,10 @@ output_mirrors(struct action_xlate_ctx *ctx, } mirrors &= ~m->dup_mirrors; + ctx->mirrors |= m->dup_mirrors; if (m->out) { output_normal(ctx, m->out, vlan); - } else if (eth_dst_may_rspan(ctx->flow.dl_dst) + } else if (eth_dst_may_rspan(orig_flow->dl_dst) && vlan != m->out_vlan) { struct ofbundle *bundle; @@ -4657,6 +5094,34 @@ output_mirrors(struct action_xlate_ctx *ctx, } } +static void +update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors, + uint64_t packets, uint64_t bytes) +{ + if (!mirrors) { + return; + } + + for (; mirrors; mirrors &= mirrors - 1) { + struct ofmirror *m; + + m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; + + if (!m) { + /* In normal circumstances 'm' will not be NULL. However, + * if mirrors are reconfigured, we can temporarily get out + * of sync in facet_revalidate(). We could "correct" the + * mirror list before reaching here, but doing that would + * not properly account the traffic stats we've currently + * accumulated for previous mirror configuration. */ + continue; + } + + m->packet_count += packets; + m->byte_count += bytes; + } +} + /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to * indicate this; newer upstream kernels use gratuitous ARP requests. */ @@ -4791,10 +5256,8 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, static void xlate_normal(struct action_xlate_ctx *ctx) { - mirror_mask_t dst_mirrors = 0; struct ofport_dpif *in_port; struct ofbundle *in_bundle; - struct ofbundle *out_bundle; struct mac_entry *mac; uint16_t vlan; uint16_t vid; @@ -4843,7 +5306,6 @@ xlate_normal(struct action_xlate_ctx *ctx) /* Check other admissibility requirements. */ if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) { - output_mirrors(ctx, vlan, in_bundle, 0); return; } @@ -4856,7 +5318,9 @@ xlate_normal(struct action_xlate_ctx *ctx) mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan, &ctx->tags); if (mac) { - out_bundle = mac->port.p; + if (mac->port.p != in_bundle) { + output_normal(ctx, mac->port.p, vlan); + } } else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) { /* If we are revalidating but don't have a learning entry then eject * the flow. Installing a flow that floods packets opens up a window @@ -4866,14 +5330,18 @@ xlate_normal(struct action_xlate_ctx *ctx) ctx->may_set_up_flow = false; return; } else { - out_bundle = OFBUNDLE_FLOOD; - } + struct ofbundle *bundle; - /* Don't send packets out their input bundles. */ - if (in_bundle != out_bundle) { - dst_mirrors = compose_dsts(ctx, vlan, in_bundle, out_bundle); + HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) { + if (bundle != in_bundle + && ofbundle_includes_vlan(bundle, vlan) + && bundle->floodable + && !bundle->mirror_out) { + output_normal(ctx, bundle, vlan); + } + } + ctx->nf_output_iface = NF_OUT_FLOOD; } - output_mirrors(ctx, vlan, in_bundle, dst_mirrors); } /* Optimized flow revalidation. @@ -5027,7 +5495,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, flow); - action_xlate_ctx_init(&ctx, ofproto, flow, packet); + action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, packet); odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions); dpif_execute(ofproto->dpif, key.data, key.size, odp_actions->data, odp_actions->size, packet); @@ -5035,6 +5503,26 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, } return error; } + +/* NetFlow. */ + +static int +set_netflow(struct ofproto *ofproto_, + const struct netflow_options *netflow_options) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (netflow_options) { + if (!ofproto->netflow) { + ofproto->netflow = netflow_create(); + } + return netflow_set_options(ofproto->netflow, netflow_options); + } else { + netflow_destroy(ofproto->netflow); + ofproto->netflow = NULL; + return 0; + } +} static void get_netflow_ids(const struct ofproto *ofproto_, @@ -5044,6 +5532,42 @@ get_netflow_ids(const struct ofproto *ofproto_, dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id); } + +static void +send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet) +{ + if (!facet_is_controller_flow(facet) && + netflow_active_timeout_expired(ofproto->netflow, &facet->nf_flow)) { + struct subfacet *subfacet; + struct ofexpired expired; + + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + if (subfacet->installed) { + struct dpif_flow_stats stats; + + subfacet_install(ofproto, subfacet, subfacet->actions, + subfacet->actions_len, &stats); + subfacet_update_stats(ofproto, subfacet, &stats); + } + } + + expired.flow = facet->flow; + expired.packet_count = facet->packet_count; + expired.byte_count = facet->byte_count; + expired.used = facet->used; + netflow_expire(ofproto->netflow, &facet->nf_flow, &expired); + } +} + +static void +send_netflow_active_timeouts(struct ofproto_dpif *ofproto) +{ + struct facet *facet; + + HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { + send_active_timeout(ofproto, facet); + } +} static struct ofproto_dpif * ofproto_dpif_lookup(const char *name) @@ -5174,6 +5698,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, struct ofpbuf odp_key; struct ofpbuf *packet; struct rule_dpif *rule; + ovs_be16 initial_tci; struct ds result; struct flow flow; char *s; @@ -5183,6 +5708,17 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, ds_init(&result); dpname = strtok_r(args, " ", &save_ptr); + if (!dpname) { + unixctl_command_reply(conn, 501, "Bad command syntax"); + goto exit; + } + + ofproto = ofproto_dpif_lookup(dpname); + if (!ofproto) { + unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list " + "for help)"); + goto exit; + } arg1 = strtok_r(NULL, " ", &save_ptr); arg2 = strtok_r(NULL, " ", &save_ptr); arg3 = strtok_r(NULL, " ", &save_ptr); @@ -5200,8 +5736,10 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, } /* Convert odp_key to flow. */ - error = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); - if (error) { + error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data, + odp_key.size, &flow, + &initial_tci); + if (error == ODP_FIT_ERROR) { unixctl_command_reply(conn, 501, "Invalid flow"); goto exit; } @@ -5240,18 +5778,12 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, free(s); flow_extract(packet, priority, tun_id, in_port, &flow); + initial_tci = flow.vlan_tci; } else { unixctl_command_reply(conn, 501, "Bad command syntax"); goto exit; } - ofproto = ofproto_dpif_lookup(dpname); - if (!ofproto) { - unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list " - "for help)"); - goto exit; - } - ds_put_cstr(&result, "Flow: "); flow_format(&result, &flow); ds_put_char(&result, '\n'); @@ -5264,7 +5796,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, trace.result = &result; trace.flow = flow; - action_xlate_ctx_init(&trace.ctx, ofproto, &flow, packet); + action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci, packet); trace.ctx.resubmit_hook = trace_resubmit; odp_actions = xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions); @@ -5330,6 +5862,148 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register("ofproto/unclog", "", ofproto_dpif_unclog, NULL); } +/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) + * + * This is deprecated. It is only for compatibility with broken device drivers + * in old versions of Linux that do not properly support VLANs when VLAN + * devices are not used. When broken device drivers are no longer in + * widespread use, we will delete these interfaces. */ + +static int +set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto); + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + + if (realdev_ofp_port == ofport->realdev_ofp_port + && vid == ofport->vlandev_vid) { + return 0; + } + + ofproto->need_revalidate = true; + + if (ofport->realdev_ofp_port) { + vsp_remove(ofport); + } + if (realdev_ofp_port && ofport->bundle) { + /* vlandevs are enslaved to their realdevs, so they are not allowed to + * themselves be part of a bundle. */ + bundle_set(ofport->up.ofproto, ofport->bundle, NULL); + } + + ofport->realdev_ofp_port = realdev_ofp_port; + ofport->vlandev_vid = vid; + + if (realdev_ofp_port) { + vsp_add(ofport, realdev_ofp_port, vid); + } + + return 0; +} + +static uint32_t +hash_realdev_vid(uint16_t realdev_ofp_port, int vid) +{ + return hash_2words(realdev_ofp_port, vid); +} + +static uint32_t +vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto, + uint32_t realdev_odp_port, ovs_be16 vlan_tci) +{ + if (!hmap_is_empty(&ofproto->realdev_vid_map)) { + uint16_t realdev_ofp_port = odp_port_to_ofp_port(realdev_odp_port); + int vid = vlan_tci_to_vid(vlan_tci); + const struct vlan_splinter *vsp; + + HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node, + hash_realdev_vid(realdev_ofp_port, vid), + &ofproto->realdev_vid_map) { + if (vsp->realdev_ofp_port == realdev_ofp_port + && vsp->vid == vid) { + return ofp_port_to_odp_port(vsp->vlandev_ofp_port); + } + } + } + return realdev_odp_port; +} + +static struct vlan_splinter * +vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port) +{ + struct vlan_splinter *vsp; + + HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_int(vlandev_ofp_port, 0), + &ofproto->vlandev_map) { + if (vsp->vlandev_ofp_port == vlandev_ofp_port) { + return vsp; + } + } + + return NULL; +} + +static uint16_t +vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto, + uint16_t vlandev_ofp_port, int *vid) +{ + if (!hmap_is_empty(&ofproto->vlandev_map)) { + const struct vlan_splinter *vsp; + + vsp = vlandev_find(ofproto, vlandev_ofp_port); + if (vsp) { + if (vid) { + *vid = vsp->vid; + } + return vsp->realdev_ofp_port; + } + } + return 0; +} + +static void +vsp_remove(struct ofport_dpif *port) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + struct vlan_splinter *vsp; + + vsp = vlandev_find(ofproto, port->up.ofp_port); + if (vsp) { + hmap_remove(&ofproto->vlandev_map, &vsp->vlandev_node); + hmap_remove(&ofproto->realdev_vid_map, &vsp->realdev_vid_node); + free(vsp); + + port->realdev_ofp_port = 0; + } else { + VLOG_ERR("missing vlan device record"); + } +} + +static void +vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + + if (!vsp_vlandev_to_realdev(ofproto, port->up.ofp_port, NULL) + && (vsp_realdev_to_vlandev(ofproto, realdev_ofp_port, htons(vid)) + == realdev_ofp_port)) { + struct vlan_splinter *vsp; + + vsp = xmalloc(sizeof *vsp); + hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node, + hash_int(port->up.ofp_port, 0)); + hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node, + hash_realdev_vid(realdev_ofp_port, vid)); + vsp->realdev_ofp_port = realdev_ofp_port; + vsp->vlandev_ofp_port = port->up.ofp_port; + vsp->vid = vid; + + port->realdev_ofp_port = realdev_ofp_port; + } else { + VLOG_ERR("duplicate vlan device record"); + } +} + const struct ofproto_class ofproto_dpif_class = { enumerate_types, enumerate_names, @@ -5378,10 +6052,13 @@ const struct ofproto_class ofproto_dpif_class = { get_stp_status, set_stp_port, get_stp_port_status, + set_queues, bundle_set, bundle_remove, mirror_set, + mirror_get_stats, set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, + set_realdev, };