X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=1b654fd9a09ef95f3cc4815da7dc012777c5b75e;hb=039d283c2f1fa4859e690087006081c733db9b33;hp=580f3bd178b15272c9404ce8dda2ff6d160d9ed2;hpb=b95fc6ba74d76e62e5ca1e90d86ff757f7f35c9b;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 580f3bd17..1b654fd9a 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -134,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. */ @@ -171,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); @@ -213,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. */ @@ -229,7 +237,7 @@ 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); @@ -276,9 +284,9 @@ struct facet { uint64_t byte_count; /* Number of bytes received. */ /* Resubmit statistics. */ - 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. */ + 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(). */ @@ -294,6 +302,7 @@ struct facet { bool has_learn; /* Actions include NXAST_LEARN? */ bool has_normal; /* Actions output to OFPP_NORMAL? */ 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 *); @@ -352,15 +361,19 @@ struct subfacet { 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); + 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 *); + const struct nlattr *key, size_t key_len); 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 *, @@ -387,11 +400,21 @@ 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 @@ -404,6 +427,27 @@ struct priority_to_dscp { 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) { @@ -464,10 +508,16 @@ struct ofproto_dpif { struct list completions; bool has_bundle_action; /* True when the first bundle action appears. */ + struct netdev_stats stats; /* To account packets generated and consumed in + * userspace. */ /* 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 @@ -494,10 +544,7 @@ static void update_learning_table(struct ofproto_dpif *, struct ofbundle *); /* Upcalls. */ #define FLOW_MISS_MAX_BATCH 50 - -static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *); -static void handle_miss_upcalls(struct ofproto_dpif *, - struct dpif_upcall *, size_t n); +static int handle_upcalls(struct ofproto_dpif *, unsigned int max_batch); /* Flow expiration. */ static int expire(struct ofproto_dpif *); @@ -506,11 +553,12 @@ static int expire(struct ofproto_dpif *); static void send_netflow_active_timeouts(struct ofproto_dpif *); /* Utilities. */ -static int send_packet(const struct ofport_dpif *, - 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); @@ -618,7 +666,11 @@ 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; + memset(&ofproto->stats, 0, sizeof ofproto->stats); return 0; } @@ -665,47 +717,56 @@ destruct(struct ofproto *ofproto_) hmap_destroy(&ofproto->facets); hmap_destroy(&ofproto->subfacets); + hmap_destroy(&ofproto->vlandev_map); + hmap_destroy(&ofproto->realdev_vid_map); + dpif_close(ofproto->dpif); } +static int +run_fast(struct ofproto *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + unsigned int work; + + /* Handle one or more batches of upcalls, until there's nothing left to do + * or until we do a fixed total amount of work. + * + * We do work in batches because it can be much cheaper to set up a number + * of flows and fire off their patches all at once. We do multiple batches + * because in some cases handling a packet can cause another packet to be + * queued almost immediately as part of the return flow. Both + * optimizations can make major improvements on some benchmarks and + * presumably for real traffic as well. */ + work = 0; + while (work < FLOW_MISS_MAX_BATCH) { + int retval = handle_upcalls(ofproto, FLOW_MISS_MAX_BATCH - work); + if (retval <= 0) { + return -retval; + } + work += retval; + } + return 0; +} + static int run(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct dpif_upcall misses[FLOW_MISS_MAX_BATCH]; struct ofport_dpif *ofport; struct ofbundle *bundle; - size_t n_misses; - int i; + int error; if (!clogged) { complete_operations(ofproto); } dpif_run(ofproto->dpif); - n_misses = 0; - for (i = 0; i < FLOW_MISS_MAX_BATCH; i++) { - struct dpif_upcall *upcall = &misses[n_misses]; - int error; - - error = dpif_recv(ofproto->dpif, upcall); - if (error) { - if (error == ENODEV && n_misses == 0) { - return error; - } - break; - } - - if (upcall->type == DPIF_UC_MISS) { - /* Handle it later. */ - n_misses++; - } else { - handle_upcall(ofproto, upcall); - } + error = run_fast(ofproto_); + if (error) { + return error; } - handle_miss_upcalls(ofproto, misses, n_misses); - if (timer_expired(&ofproto->next_expiration)) { int delay = expire(ofproto); timer_set_duration(&ofproto->next_expiration, delay); @@ -876,10 +937,11 @@ port_construct(struct ofport *port_) 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, - netdev_get_name(port->up.netdev)); + dpif_sflow_add_port(ofproto->sflow, port_); } return 0; @@ -942,8 +1004,7 @@ set_sflow(struct ofproto *ofproto_, ds = ofproto->sflow = dpif_sflow_create(ofproto->dpif); HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { - dpif_sflow_add_port(ds, ofport->odp_port, - netdev_get_name(ofport->up.netdev)); + dpif_sflow_add_port(ds, &ofport->up); } ofproto->need_revalidate = true; } @@ -1987,6 +2048,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) { @@ -2131,6 +2210,63 @@ port_del(struct ofproto *ofproto_, uint16_t ofp_port) return error; } +static int +port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + int error; + + error = netdev_get_stats(ofport->up.netdev, stats); + + if (!error && ofport->odp_port == OVSP_LOCAL) { + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + + /* ofproto->stats.tx_packets represents packets that we created + * internally and sent to some port (e.g. packets sent with + * send_packet()). Account for them as if they had come from + * OFPP_LOCAL and got forwarded. */ + + if (stats->rx_packets != UINT64_MAX) { + stats->rx_packets += ofproto->stats.tx_packets; + } + + if (stats->rx_bytes != UINT64_MAX) { + stats->rx_bytes += ofproto->stats.tx_bytes; + } + + /* ofproto->stats.rx_packets represents packets that were received on + * some port and we processed internally and dropped (e.g. STP). + * Account fro them as if they had been forwarded to OFPP_LOCAL. */ + + if (stats->tx_packets != UINT64_MAX) { + stats->tx_packets += ofproto->stats.rx_packets; + } + + if (stats->tx_bytes != UINT64_MAX) { + stats->tx_bytes += ofproto->stats.rx_bytes; + } + } + + return error; +} + +/* Account packets for LOCAL port. */ +static void +ofproto_update_local_port_stats(const struct ofproto *ofproto_, + size_t tx_size, size_t rx_size) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (rx_size) { + ofproto->stats.rx_packets++; + ofproto->stats.rx_bytes += rx_size; + } + if (tx_size) { + ofproto->stats.tx_packets++; + ofproto->stats.tx_bytes += tx_size; + } +} + struct port_dump_state { struct dpif_port_dump dump; bool done; @@ -2217,6 +2353,7 @@ struct flow_miss { enum odp_key_fitness key_fitness; const struct nlattr *key; size_t key_len; + ovs_be16 initial_tci; struct list packets; }; @@ -2306,7 +2443,8 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, static struct flow_miss * flow_miss_create(struct hmap *todo, const struct flow *flow, enum odp_key_fitness key_fitness, - const struct nlattr *key, size_t key_len) + const struct nlattr *key, size_t key_len, + ovs_be16 initial_tci) { uint32_t hash = flow_hash(flow, 0); struct flow_miss *miss; @@ -2323,6 +2461,7 @@ flow_miss_create(struct hmap *todo, const struct 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; } @@ -2368,7 +2507,8 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, } subfacet = subfacet_create(ofproto, facet, - miss->key_fitness, miss->key, miss->key_len); + 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); @@ -2425,6 +2565,37 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, } } +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) @@ -2450,12 +2621,16 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, 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 metadata and check userspace/kernel agreement on flow match, * then set 'flow''s header pointers. */ - fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + fitness = ofproto_dpif_extract_flow_key(ofproto, + upcall->key, upcall->key_len, + &flow, &initial_tci); if (fitness == ODP_FIT_ERROR) { + ofpbuf_delete(upcall->packet); continue; } flow_extract(upcall->packet, flow.priority, flow.tun_id, @@ -2463,6 +2638,8 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, /* Handle 802.1ag, LACP, and STP specially. */ if (process_special(ofproto, &flow, upcall->packet)) { + ofproto_update_local_port_stats(&ofproto->up, + 0, upcall->packet->size); ofpbuf_delete(upcall->packet); ofproto->n_matches++; continue; @@ -2470,7 +2647,7 @@ 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, fitness, - upcall->key, upcall->key_len); + upcall->key, upcall->key_len, initial_tci); list_push_back(&miss->packets, &upcall->packet->list_node); } @@ -2521,45 +2698,77 @@ 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) { + ofpbuf_delete(upcall->packet); + 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 { VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata); + ofpbuf_delete(upcall->packet); } } -static void -handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +static int +handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch) { - switch (upcall->type) { - case DPIF_UC_ACTION: - handle_userspace_upcall(ofproto, upcall); - break; + struct dpif_upcall misses[FLOW_MISS_MAX_BATCH]; + int n_misses; + int i; - case DPIF_UC_MISS: - /* The caller handles these. */ - NOT_REACHED(); + assert (max_batch <= FLOW_MISS_MAX_BATCH); - case DPIF_N_UC_TYPES: - default: - VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type); - break; + n_misses = 0; + for (i = 0; i < max_batch; i++) { + struct dpif_upcall *upcall = &misses[n_misses]; + int error; + + error = dpif_recv(ofproto->dpif, upcall); + if (error) { + break; + } + + switch (upcall->type) { + case DPIF_UC_ACTION: + handle_userspace_upcall(ofproto, upcall); + break; + + case DPIF_UC_MISS: + /* Handle it later. */ + n_misses++; + break; + + case DPIF_N_UC_TYPES: + default: + VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, + upcall->type); + break; + } } + + handle_miss_upcalls(ofproto, misses, n_misses); + + return i; } /* Flow expiration. */ @@ -2635,16 +2844,9 @@ 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)) { - enum odp_key_fitness fitness; struct subfacet *subfacet; - struct flow flow; - fitness = odp_flow_key_to_flow(key, key_len, &flow); - if (fitness == ODP_FIT_ERROR) { - continue; - } - - subfacet = subfacet_find(p, key, key_len, &flow); + subfacet = subfacet_find(p, key, key_len); if (subfacet && subfacet->installed) { struct facet *facet = subfacet->facet; @@ -2668,9 +2870,18 @@ update_stats(struct ofproto_dpif *p) facet_account(p, facet); facet_push_stats(facet); } else { + if (!VLOG_DROP_WARN(&rl)) { + struct ds s; + + ds_init(&s); + odp_flow_key_format(key, key_len, &s); + VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s)); + ds_destroy(&s); + } + + COVERAGE_INC(facet_unexpected); /* 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); } } @@ -2947,7 +3158,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)); @@ -3135,7 +3347,8 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) struct ofpbuf *odp_actions; bool should_install; - action_xlate_ctx_init(&ctx, ofproto, &facet->flow, NULL); + 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 @@ -3177,6 +3390,7 @@ 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; + facet->mirrors = ctx.mirrors; if (new_actions) { i = 0; LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { @@ -3195,7 +3409,7 @@ 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; } return true; @@ -3221,30 +3435,33 @@ 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); } } @@ -3268,7 +3485,7 @@ 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, const struct flow *flow, uint64_t packets, uint64_t bytes, @@ -3281,7 +3498,7 @@ 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)); @@ -3319,7 +3536,7 @@ subfacet_find__(struct ofproto_dpif *ofproto, 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) + 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; @@ -3346,6 +3563,7 @@ subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, subfacet->key_len = key_len; } subfacet->installed = false; + subfacet->initial_tci = initial_tci; return subfacet; } @@ -3354,12 +3572,18 @@ subfacet_create(struct ofproto_dpif *ofproto, struct facet *facet, * '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) + const struct nlattr *key, size_t key_len) { uint32_t key_hash = odp_flow_key_hash(key, key_len); + enum odp_key_fitness fitness; + struct flow flow; + + fitness = odp_flow_key_to_flow(key, key_len, &flow); + if (fitness == ODP_FIT_ERROR) { + return NULL; + } - return subfacet_find__(ofproto, key, key_len, key_hash, flow); + return subfacet_find__(ofproto, key, key_len, key_hash, &flow); } /* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from @@ -3413,13 +3637,15 @@ subfacet_make_actions(struct ofproto_dpif *p, struct subfacet *subfacet, struct ofpbuf *odp_actions; struct action_xlate_ctx ctx; - action_xlate_ctx_init(&ctx, p, &facet->flow, packet); + 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)) { @@ -3690,7 +3916,7 @@ rule_execute(struct rule *rule_, const struct flow *flow, struct ofpbuf *odp_actions; size_t size; - 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, @@ -3723,18 +3949,26 @@ rule_modify_actions(struct rule *rule_) } /* Sends 'packet' out 'ofport'. + * May modify 'packet'. * Returns 0 if successful, otherwise a positive errno value. */ static int -send_packet(const struct ofport_dpif *ofport, 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); - uint16_t odp_port = ofport->odp_port; 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); @@ -3752,6 +3986,7 @@ send_packet(const struct ofport_dpif *ofport, const struct ofpbuf *packet) VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)", ofproto->up.name, odp_port, strerror(error)); } + ofproto_update_local_port_stats(ofport->up.ofproto, packet->size, 0); return error; } @@ -3862,172 +4097,15 @@ fix_sflow_action(struct action_xlate_ctx *ctx) cookie->vlan_tci = base->vlan_tci; } -static void -commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, - const void *key, size_t key_size) -{ - size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); - nl_msg_put_unspec(odp_actions, key_type, key, key_size); - nl_msg_end_nested(odp_actions, offset); -} - -static void -commit_set_tun_id_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->tun_id == flow->tun_id) { - return; - } - base->tun_id = flow->tun_id; - - commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, - &base->tun_id, sizeof(base->tun_id)); -} - -static void -commit_set_ether_addr_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - struct ovs_key_ethernet eth_key; - - if (eth_addr_equals(base->dl_src, flow->dl_src) && - eth_addr_equals(base->dl_dst, flow->dl_dst)) { - return; - } - - memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); - memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); - - memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN); - memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN); - - commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, - ð_key, sizeof(eth_key)); -} - -static void -commit_vlan_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->vlan_tci == flow->vlan_tci) { - return; - } - - if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } - - if (flow->vlan_tci & htons(VLAN_CFI)) { - struct ovs_action_push_vlan vlan; - - vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = flow->vlan_tci; - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - &vlan, sizeof vlan); - } - base->vlan_tci = flow->vlan_tci; -} - -static void -commit_set_nw_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - struct ovs_key_ipv4 ipv4_key; - - if (base->dl_type != htons(ETH_TYPE_IP) || - !base->nw_src || !base->nw_dst) { - return; - } - - if (base->nw_src == flow->nw_src && - base->nw_dst == flow->nw_dst && - base->nw_tos == flow->nw_tos && - base->nw_ttl == flow->nw_ttl && - base->nw_frag == flow->nw_frag) { - return; - } - - 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_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE - : base->nw_frag == FLOW_NW_FRAG_ANY - ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); - - commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4, - &ipv4_key, sizeof(ipv4_key)); -} - -static void -commit_set_port_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (!base->tp_src || !base->tp_dst) { - return; - } - - if (base->tp_src == flow->tp_src && - base->tp_dst == flow->tp_dst) { - return; - } - - if (flow->nw_proto == IPPROTO_TCP) { - struct ovs_key_tcp port_key; - - port_key.tcp_src = base->tp_src = flow->tp_src; - port_key.tcp_dst = base->tp_dst = flow->tp_dst; - - commit_set_action(odp_actions, OVS_KEY_ATTR_TCP, - &port_key, sizeof(port_key)); - - } else if (flow->nw_proto == IPPROTO_UDP) { - struct ovs_key_udp port_key; - - port_key.udp_src = base->tp_src = flow->tp_src; - port_key.udp_dst = base->tp_dst = flow->tp_dst; - - commit_set_action(odp_actions, OVS_KEY_ATTR_UDP, - &port_key, sizeof(port_key)); - } -} - -static void -commit_set_priority_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) -{ - if (base->priority == flow->priority) { - return; - } - base->priority = flow->priority; - - commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY, - &base->priority, sizeof(base->priority)); -} - -static void -commit_odp_actions(struct action_xlate_ctx *ctx) -{ - const struct flow *flow = &ctx->flow; - struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; - - commit_set_tun_id_action(flow, base, odp_actions); - commit_set_ether_addr_action(flow, base, odp_actions); - 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 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; @@ -4048,11 +4126,18 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, * later and we're pre-populating the flow table. */ } - commit_odp_actions(ctx); - nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); + 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->flow, &ctx->base_flow, ctx->odp_actions); + 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; } @@ -4135,7 +4220,6 @@ 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; @@ -4158,7 +4242,7 @@ compose_controller_action(struct action_xlate_ctx *ctx, int len) { struct user_action_cookie cookie; - commit_odp_actions(ctx); + commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions); cookie.type = USER_ACTION_COOKIE_CONTROLLER; cookie.data = len; cookie.n_output = 0; @@ -4562,10 +4646,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; @@ -4575,6 +4662,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); @@ -4584,10 +4673,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; @@ -4628,6 +4716,7 @@ xlate_actions(struct action_xlate_ctx *ctx, compose_output_action(ctx, OFPP_LOCAL); } } + add_mirror_actions(ctx, &orig_flow); fix_sflow_action(ctx); } @@ -4804,34 +4893,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) { @@ -4880,18 +4941,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; @@ -4903,9 +5014,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; @@ -4919,6 +5031,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. */ @@ -5053,10 +5193,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; @@ -5105,7 +5243,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; } @@ -5118,7 +5255,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 @@ -5128,14 +5267,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. @@ -5289,7 +5432,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); @@ -5492,6 +5635,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; @@ -5501,6 +5645,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); @@ -5518,8 +5673,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; } @@ -5558,18 +5715,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'); @@ -5582,7 +5733,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); @@ -5648,6 +5799,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, @@ -5657,6 +5950,7 @@ const struct ofproto_class ofproto_dpif_class = { destruct, dealloc, run, + run_fast, wait, flush, get_features, @@ -5670,6 +5964,7 @@ const struct ofproto_class ofproto_dpif_class = { port_query_by_name, port_add, port_del, + port_get_stats, port_dump_start, port_dump_next, port_dump_done, @@ -5700,7 +5995,9 @@ const struct ofproto_class ofproto_dpif_class = { bundle_set, bundle_remove, mirror_set, + mirror_get_stats, set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, + set_realdev, };