X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=ofproto%2Fofproto-dpif.c;h=6af49747fa851f14958e18a32e02d3ee4b224fa9;hb=a6b7506dab305d91fc5f2ac6416a714e5fa09dd4;hp=69625cf6ec2f919eb387195c24f187f5f3a1d44a;hpb=419460f4e1465102bb9699e1e470b4a1349999b1;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 69625cf6e..6af49747f 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -25,6 +25,7 @@ #include "bond.h" #include "bundle.h" #include "byte-order.h" +#include "connectivity.h" #include "connmgr.h" #include "coverage.h" #include "cfm.h" @@ -57,6 +58,7 @@ #include "ofproto-dpif-upcall.h" #include "ofproto-dpif-xlate.h" #include "poll-loop.h" +#include "seq.h" #include "simap.h" #include "smap.h" #include "timer.h" @@ -110,6 +112,28 @@ struct rule_dpif { static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes); static struct rule_dpif *rule_dpif_cast(const struct rule *); +struct group_dpif { + struct ofgroup up; + + /* These statistics: + * + * - Do include packets and bytes from facets that have been deleted or + * whose own statistics have been folded into the rule. + * + * - Do include packets and bytes sent "by hand" that were accounted to + * the rule without any facet being involved (this is a rare corner + * case in rule_execute()). + * + * - 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 subfacet. + */ + struct ovs_mutex stats_mutex; + uint64_t packet_count OVS_GUARDED; /* Number of packets received. */ + uint64_t byte_count OVS_GUARDED; /* Number of bytes received. */ + struct bucket_counter *bucket_stats OVS_GUARDED; /* Bucket statistics. */ +}; + struct ofbundle { struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */ struct ofproto_dpif *ofproto; /* Owning ofproto. */ @@ -191,7 +215,8 @@ struct subfacet { #define SUBFACET_DESTROY_MAX_BATCH 50 -static struct subfacet *subfacet_create(struct facet *, struct flow_miss *); +static struct subfacet *subfacet_create(struct facet *, struct flow_miss *, + uint32_t key_hash); static struct subfacet *subfacet_find(struct dpif_backer *, const struct nlattr *key, size_t key_len, uint32_t key_hash); @@ -261,9 +286,7 @@ struct facet { 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. */ - uint8_t tcp_flags; /* TCP flags seen for this 'rule'. */ + uint16_t tcp_flags; /* TCP flags seen for this 'rule'. */ struct xlate_out xout; @@ -294,7 +317,6 @@ static void flow_push_stats(struct ofproto_dpif *, struct flow *, struct dpif_flow_stats *, bool may_learn); static void facet_push_stats(struct facet *, bool may_learn); static void facet_learn(struct facet *); -static void facet_account(struct facet *); static void push_all_stats(void); static bool facet_is_controller_flow(struct facet *); @@ -310,6 +332,7 @@ struct ofport_dpif { struct bfd *bfd; /* BFD, if any. */ bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* This port is a tunnel. */ + bool is_layer3; /* This is a layer 3 port. */ long long int carrier_seq; /* Carrier status changes. */ struct ofport_dpif *peer; /* Peer if patch port. */ @@ -365,8 +388,6 @@ static void port_run(struct ofport_dpif *); static int set_bfd(struct ofport *, const struct smap *); static int set_cfm(struct ofport *, const struct cfm_settings *); static void ofport_update_peer(struct ofport_dpif *); -static void run_fast_rl(void); -static int run_fast(struct ofproto *); struct dpif_completion { struct list list_node; @@ -398,11 +419,6 @@ COVERAGE_DEFINE(rev_flow_table); COVERAGE_DEFINE(rev_mac_learning); COVERAGE_DEFINE(rev_inconsistency); -struct avg_subfacet_rates { - double add_rate; /* Moving average of new flows created per minute. */ - double del_rate; /* Moving average of flows deleted per minute. */ -}; - /* All datapaths of a given type share a single dpif backer instance. */ struct dpif_backer { char *type; @@ -412,7 +428,7 @@ struct dpif_backer { struct timer next_expiration; struct ovs_rwlock odp_to_ofport_lock; - struct hmap odp_to_ofport_map OVS_GUARDED; /* ODP port to ofport map. */ + struct hmap odp_to_ofport_map OVS_GUARDED; /* Contains "struct ofport"s. */ struct simap tnl_backers; /* Set of dpif ports backing tunnels. */ @@ -432,7 +448,6 @@ struct dpif_backer { * exposed via "ovs-appctl dpif/show". The goal is to learn about * traffic patterns in ways that we can use later to improve Open vSwitch * performance in new situations. */ - long long int created; /* Time when it is created. */ unsigned max_n_subfacet; /* Maximum number of flows */ unsigned avg_n_subfacet; /* Average number of flows. */ long long int avg_subfacet_life; /* Average life span of subfacets. */ @@ -463,6 +478,7 @@ struct ofproto_dpif { struct hmap bundles; /* Contains "struct ofbundle"s. */ struct mac_learning *ml; bool has_bonded_bundles; + bool lacp_enabled; struct mbridge *mbridge; /* Facets. */ @@ -487,6 +503,7 @@ struct ofproto_dpif { struct sset ghost_ports; /* Ports with no datapath port. */ struct sset port_poll_set; /* Queued names for port_poll() reply. */ int port_poll_errno; /* Last errno for port_poll() reply. */ + uint64_t change_seq; /* Connectivity status changes. */ /* Per ofproto's dpif stats. */ uint64_t n_hit; @@ -515,7 +532,9 @@ ofproto_dpif_cast(const struct ofproto *ofproto) static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port); static void ofproto_trace(struct ofproto_dpif *, const struct flow *, - const struct ofpbuf *packet, struct ds *); + const struct ofpbuf *packet, + const struct ofpact[], size_t ofpacts_len, + struct ds *); /* Upcalls. */ static void handle_upcalls(struct dpif_backer *); @@ -523,9 +542,6 @@ static void handle_upcalls(struct dpif_backer *); /* Flow expiration. */ static int expire(struct dpif_backer *); -/* NetFlow. */ -static void send_netflow_active_timeouts(struct ofproto_dpif *); - /* Global variables. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -545,11 +561,11 @@ ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto, * Takes ownership of 'pin' and pin->packet. */ void ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto, - struct ofputil_packet_in *pin) + struct ofproto_packet_in *pin) { if (!guarded_list_push_back(&ofproto->pins, &pin->list_node, 1024)) { COVERAGE_INC(packet_in_overflow); - free(CONST_CAST(void *, pin->packet)); + free(CONST_CAST(void *, pin->up.packet)); free(pin); } } @@ -653,6 +669,8 @@ type_run(const char *type) dpif_run(backer->dpif); + handle_upcalls(backer); + /* The most natural place to push facet statistics is when they're pulled * from the datapath. However, when there are many flows in the datapath, * this expensive operation can occur so frequently, that it reduces our @@ -780,10 +798,9 @@ type_run(const char *type) ofproto->no_packet_in_rule, ofproto->ml, ofproto->stp, ofproto->mbridge, ofproto->sflow, ofproto->ipfix, - ofproto->up.frag_handling, + ofproto->netflow, ofproto->up.frag_handling, ofproto->up.forward_bpdu, - connmgr_has_in_band(ofproto->up.connmgr), - ofproto->netflow != NULL); + connmgr_has_in_band(ofproto->up.connmgr)); HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { xlate_bundle_set(ofproto, bundle, bundle->name, @@ -802,8 +819,8 @@ type_run(const char *type) ofport->up.netdev, ofport->cfm, ofport->bfd, ofport->peer, stp_port, ofport->qdscp, ofport->n_qdscp, - ofport->up.pp.config, ofport->is_tunnel, - ofport->may_enable); + ofport->up.pp.config, ofport->up.pp.state, + ofport->is_tunnel, ofport->may_enable); } ovs_rwlock_unlock(&xlate_rwlock); @@ -814,7 +831,6 @@ type_run(const char *type) ovs_rwlock_unlock(&ofproto->facets.rwlock); CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) { facet_revalidate(facet); - run_fast_rl(); } } @@ -981,44 +997,6 @@ process_dpif_port_error(struct dpif_backer *backer, int error) } } -static int -dpif_backer_run_fast(struct dpif_backer *backer) -{ - handle_upcalls(backer); - - return 0; -} - -static int -type_run_fast(const char *type) -{ - struct dpif_backer *backer; - - backer = shash_find_data(&all_dpif_backers, type); - if (!backer) { - /* This is not necessarily a problem, since backers are only - * created on demand. */ - return 0; - } - - return dpif_backer_run_fast(backer); -} - -static void -run_fast_rl(void) -{ - static long long int port_rl = LLONG_MIN; - - if (time_msec() >= port_rl) { - struct ofproto_dpif *ofproto; - - HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { - run_fast(&ofproto->up); - } - port_rl = time_msec() + 200; - } -} - static void type_wait(const char *type) { @@ -1199,7 +1177,6 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp) backer->n_handler_threads = n_handler_threads; backer->max_n_subfacet = 0; - backer->created = time_msec(); backer->avg_n_subfacet = 0; backer->avg_subfacet_life = 0; @@ -1211,7 +1188,6 @@ construct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct shash_node *node, *next; - uint32_t max_ports; int error; error = open_dpif_backer(ofproto->up.type, &ofproto->backer); @@ -1219,9 +1195,6 @@ construct(struct ofproto *ofproto_) return error; } - max_ports = dpif_get_max_ports(ofproto->backer->dpif); - ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX))); - ofproto->netflow = NULL; ofproto->sflow = NULL; ofproto->ipfix = NULL; @@ -1230,10 +1203,11 @@ construct(struct ofproto *ofproto_) ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME); ofproto->mbridge = mbridge_create(); ofproto->has_bonded_bundles = false; + ofproto->lacp_enabled = false; ovs_mutex_init(&ofproto->stats_mutex); ovs_mutex_init(&ofproto->vsp_mutex); - classifier_init(&ofproto->facets); + classifier_init(&ofproto->facets, NULL); ofproto->consistency_rl = LLONG_MIN; guarded_list_init(&ofproto->pins); @@ -1247,6 +1221,7 @@ construct(struct ofproto *ofproto_) sset_init(&ofproto->ghost_ports); sset_init(&ofproto->port_poll_set); ofproto->port_poll_errno = 0; + ofproto->change_seq = 0; SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) { struct iface_hint *iface_hint = node->data; @@ -1359,7 +1334,7 @@ destruct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct rule_dpif *rule, *next_rule; - struct ofputil_packet_in *pin, *next_pin; + struct ofproto_packet_in *pin, *next_pin; struct facet *facet, *next_facet; struct cls_cursor cursor; struct oftable *table; @@ -1397,14 +1372,14 @@ destruct(struct ofproto *ofproto_) guarded_list_pop_all(&ofproto->pins, &pins); LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) { list_remove(&pin->list_node); - free(CONST_CAST(void *, pin->packet)); + free(CONST_CAST(void *, pin->up.packet)); free(pin); } guarded_list_destroy(&ofproto->pins); mbridge_unref(ofproto->mbridge); - netflow_destroy(ofproto->netflow); + netflow_unref(ofproto->netflow); dpif_sflow_unref(ofproto->sflow); hmap_destroy(&ofproto->bundles); mac_learning_unref(ofproto->ml); @@ -1424,38 +1399,11 @@ destruct(struct ofproto *ofproto_) close_dpif_backer(ofproto->backer); } -static int -run_fast(struct ofproto *ofproto_) -{ - struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct ofputil_packet_in *pin, *next_pin; - struct list pins; - - /* Do not perform any periodic activity required by 'ofproto' while - * waiting for flow restore to complete. */ - if (ofproto_get_flow_restore_wait()) { - return 0; - } - - guarded_list_pop_all(&ofproto->pins, &pins); - LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) { - connmgr_send_packet_in(ofproto->up.connmgr, pin); - list_remove(&pin->list_node); - free(CONST_CAST(void *, pin->packet)); - free(pin); - } - - ofproto_dpif_monitor_run_fast(); - return 0; -} - static int run(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct ofport_dpif *ofport; - struct ofbundle *bundle; - int error; + uint64_t new_seq; if (mbridge_need_revalidate(ofproto->mbridge)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; @@ -1464,21 +1412,23 @@ run(struct ofproto *ofproto_) ovs_rwlock_unlock(&ofproto->ml->rwlock); } - /* Do not perform any periodic activity below required by 'ofproto' while + /* Do not perform any periodic activity required by 'ofproto' while * waiting for flow restore to complete. */ - if (ofproto_get_flow_restore_wait()) { - return 0; - } + if (!ofproto_get_flow_restore_wait()) { + struct ofproto_packet_in *pin, *next_pin; + struct list pins; - error = run_fast(ofproto_); - if (error) { - return error; + guarded_list_pop_all(&ofproto->pins, &pins); + LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) { + connmgr_send_packet_in(ofproto->up.connmgr, pin); + list_remove(&pin->list_node); + free(CONST_CAST(void *, pin->up.packet)); + free(pin); + } } if (ofproto->netflow) { - if (netflow_run(ofproto->netflow)) { - send_netflow_active_timeouts(ofproto); - } + netflow_run(ofproto->netflow); } if (ofproto->sflow) { dpif_sflow_run(ofproto->sflow); @@ -1487,14 +1437,22 @@ run(struct ofproto *ofproto_) dpif_ipfix_run(ofproto->ipfix); } - ofproto_dpif_monitor_run_fast(); - ofproto_dpif_monitor_run(); + new_seq = seq_read(connectivity_seq_get()); + if (ofproto->change_seq != new_seq) { + struct ofport_dpif *ofport; + + HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { + port_run(ofport); + } - HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { - port_run(ofport); + ofproto->change_seq = new_seq; } - HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { - bundle_run(bundle); + if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) { + struct ofbundle *bundle; + + HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { + bundle_run(bundle); + } } stp_run(ofproto); @@ -1509,14 +1467,14 @@ run(struct ofproto *ofproto_) if (time_msec() >= ofproto->consistency_rl && !classifier_is_empty(&ofproto->facets) && !ofproto->backer->need_revalidate) { - struct cls_table *table; + struct cls_subtable *table; struct cls_rule *cr; struct facet *facet; ofproto->consistency_rl = time_msec() + 250; - table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables), - struct cls_table, hmap_node); + table = CONTAINER_OF(hmap_random_node(&ofproto->facets.subtables), + struct cls_subtable, hmap_node); cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule, hmap_node); facet = CONTAINER_OF(cr, struct facet, cr); @@ -1534,7 +1492,6 @@ static void wait(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct ofbundle *bundle; if (ofproto_get_flow_restore_wait()) { return; @@ -1546,9 +1503,12 @@ wait(struct ofproto *ofproto_) if (ofproto->ipfix) { dpif_ipfix_wait(ofproto->ipfix); } - ofproto_dpif_monitor_wait(); - HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { - bundle_wait(bundle); + if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) { + struct ofbundle *bundle; + + HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { + bundle_wait(bundle); + } } if (ofproto->netflow) { netflow_wait(ofproto->netflow); @@ -1693,6 +1653,7 @@ port_construct(struct ofport *port_) port->realdev_ofp_port = 0; port->vlandev_vid = 0; port->carrier_seq = netdev_get_carrier_resets(netdev); + port->is_layer3 = netdev_vport_is_layer3(netdev); if (netdev_vport_is_patch(netdev)) { /* By bailing out here, we don't submit the port to the sFlow module @@ -1936,6 +1897,7 @@ get_cfm_status(const struct ofport *ofport_, if (ofport->cfm) { status->faults = cfm_get_fault(ofport->cfm); + status->flap_count = cfm_get_flap_count(ofport->cfm); status->remote_opstate = cfm_get_opup(ofport->cfm); status->health = cfm_get_health(ofport->cfm); cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps); @@ -2160,6 +2122,24 @@ get_stp_port_status(struct ofport *ofport_, s->state = stp_port_get_state(sp); s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000; s->role = stp_port_get_role(sp); + + return 0; +} + +static int +get_stp_port_stats(struct ofport *ofport_, + struct ofproto_port_stp_stats *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct stp_port *sp = ofport->stp_port; + + if (!ofproto->stp || !sp) { + s->enabled = false; + return 0; + } + + s->enabled = true; stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count); return 0; @@ -2289,6 +2269,7 @@ bundle_update(struct ofbundle *bundle) bundle->floodable = true; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD + || port->is_layer3 || !stp_forward_in_state(port->stp_state)) { bundle->floodable = false; break; @@ -2336,6 +2317,7 @@ bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port, port->bundle = bundle; list_push_back(&bundle->ports, &port->bundle_node); if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD + || port->is_layer3 || !stp_forward_in_state(port->stp_state)) { bundle->floodable = false; } @@ -2428,6 +2410,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, /* LACP. */ if (s->lacp) { + ofproto->lacp_enabled = true; if (!bundle->lacp) { ofproto->backer->need_revalidate = REV_RECONFIGURE; bundle->lacp = lacp_create(); @@ -2775,13 +2758,6 @@ get_ofp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port) return ofport ? ofport_dpif_cast(ofport) : NULL; } -static struct ofport_dpif * -get_odp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port) -{ - struct ofport_dpif *port = odp_port_to_ofport(ofproto->backer, odp_port); - return port && &ofproto->up == port->up.ofproto ? port : NULL; -} - static void ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto, struct ofproto_port *ofproto_port, @@ -3202,13 +3178,32 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, { enum subfacet_path want_path; struct subfacet *subfacet; + uint32_t key_hash; + /* Update facet stats. */ facet->packet_count += miss->stats.n_packets; facet->prev_packet_count += miss->stats.n_packets; facet->byte_count += miss->stats.n_bytes; facet->prev_byte_count += miss->stats.n_bytes; - want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH; + /* Look for an existing subfacet. If we find one, update its used time. */ + key_hash = odp_flow_key_hash(miss->key, miss->key_len); + if (!list_is_empty(&facet->subfacets)) { + subfacet = subfacet_find(miss->ofproto->backer, + miss->key, miss->key_len, key_hash); + if (subfacet) { + if (subfacet->facet == facet) { + subfacet->used = MAX(subfacet->used, miss->stats.used); + } else { + /* This shouldn't happen. */ + VLOG_ERR_RL(&rl, "subfacet with wrong facet"); + subfacet_destroy(subfacet); + subfacet = NULL; + } + } + } else { + subfacet = NULL; + } /* Don't install the flow if it's the result of the "userspace" * action for an already installed facet. This can occur when a @@ -3220,7 +3215,13 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, return; } - subfacet = subfacet_create(facet, miss); + /* Create a subfacet, if we don't already have one. */ + if (!subfacet) { + subfacet = subfacet_create(facet, miss, key_hash); + } + + /* Install the subfacet, if it's not already installed. */ + want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH; if (subfacet->path != want_path) { struct flow_miss_op *op = &ops[(*n_ops)++]; struct dpif_flow_put *put = &op->dpif_op.u.flow_put; @@ -3512,10 +3513,8 @@ update_subfacet_stats(struct subfacet *subfacet, subfacet->dp_byte_count = stats->n_bytes; subfacet_update_stats(subfacet, &diff); - if (facet->accounted_bytes < facet->byte_count) { + if (diff.n_packets) { facet_learn(facet); - facet_account(facet); - facet->accounted_bytes = facet->byte_count; } } @@ -3584,7 +3583,6 @@ update_stats(struct dpif_backer *backer) delete_unexpected_flow(backer, key, key_len); break; } - run_fast_rl(); } dpif_flow_dump_done(&dump); } @@ -3777,8 +3775,6 @@ facet_create(const struct flow_miss *miss) facet->learn_rl = time_msec() + 500; list_init(&facet->subfacets); - netflow_flow_init(&facet->nf_flow); - netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used); xlate_out_copy(&facet->xout, &miss->xout); @@ -3788,7 +3784,6 @@ facet_create(const struct flow_miss *miss) classifier_insert(&ofproto->facets, &facet->cr); ovs_rwlock_unlock(&ofproto->facets.rwlock); - facet->nf_flow.output_iface = facet->xout.nf_output_iface; return facet; } @@ -3908,54 +3903,6 @@ facet_learn(struct facet *facet) facet_push_stats(facet, true); } -static void -facet_account(struct facet *facet) -{ - const struct nlattr *a; - unsigned int left; - ovs_be16 vlan_tci; - uint64_t n_bytes; - - if (!facet->xout.has_normal || !facet->ofproto->has_bonded_bundles) { - return; - } - n_bytes = facet->byte_count - facet->accounted_bytes; - - /* This loop feeds byte counters to bond_account() for rebalancing to use - * 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.) - * - * We use the actions from an arbitrary subfacet because they should all - * be equally valid for our purpose. */ - vlan_tci = facet->flow.vlan_tci; - NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->xout.odp_actions.data, - facet->xout.odp_actions.size) { - const struct ovs_action_push_vlan *vlan; - struct ofport_dpif *port; - - switch (nl_attr_type(a)) { - case OVS_ACTION_ATTR_OUTPUT: - port = get_odp_port(facet->ofproto, nl_attr_get_odp_port(a)); - if (port && port->bundle && port->bundle->bond) { - bond_account(port->bundle->bond, &facet->flow, - vlan_tci_to_vid(vlan_tci), n_bytes); - } - break; - - case OVS_ACTION_ATTR_POP_VLAN: - vlan_tci = htons(0); - break; - - case OVS_ACTION_ATTR_PUSH_VLAN: - vlan = nl_attr_get(a); - vlan_tci = vlan->vlan_tci; - break; - } - } -} - /* 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). */ @@ -4002,25 +3949,15 @@ facet_flush_stats(struct facet *facet) } facet_push_stats(facet, false); - if (facet->accounted_bytes < facet->byte_count) { - facet_account(facet); - facet->accounted_bytes = facet->byte_count; - } if (ofproto->netflow && !facet_is_controller_flow(facet)) { - struct ofexpired expired; - 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); + netflow_expire(ofproto->netflow, &facet->flow); + netflow_flow_clear(ofproto->netflow, &facet->flow); } /* Reset counters to prevent double counting if 'facet' ever gets * reinstalled. */ facet_reset_counters(facet); - - netflow_flow_clear(&facet->nf_flow); facet->tcp_flags = 0; } @@ -4135,7 +4072,7 @@ facet_revalidate(struct facet *facet) error = xlate_receive(ofproto->backer, NULL, subfacet->key, subfacet->key_len, &recv_flow, NULL, - &recv_ofproto, NULL); + &recv_ofproto, NULL, NULL, NULL, NULL); if (error || recv_ofproto != ofproto || facet != facet_find(ofproto, &recv_flow)) { @@ -4155,6 +4092,10 @@ facet_revalidate(struct facet *facet) xlate_in_init(&xin, ofproto, &facet->flow, new_rule, 0, NULL); xlate_actions(&xin, &xout); flow_wildcards_or(&xout.wc, &xout.wc, &wc); + /* Make sure non -packet fields are not masked. If not cleared, + * the memcmp() below may fail, causing an otherwise valid facet + * to be removed. */ + flow_wildcards_clear_non_packet_fields(&xout.wc); /* A facet's slow path reason should only change under dramatic * circumstances. Rather than try to update everything, it's simpler to @@ -4196,7 +4137,6 @@ facet_revalidate(struct facet *facet) facet->xout.has_fin_timeout = xout.has_fin_timeout; facet->xout.nf_output_iface = xout.nf_output_iface; facet->xout.mirrors = xout.mirrors; - facet->nf_flow.output_iface = facet->xout.nf_output_iface; ovs_mutex_lock(&new_rule->up.mutex); facet->used = MAX(facet->used, new_rule->up.created); @@ -4214,21 +4154,14 @@ facet_reset_counters(struct facet *facet) facet->byte_count = 0; facet->prev_packet_count = 0; facet->prev_byte_count = 0; - facet->accounted_bytes = 0; } static void flow_push_stats(struct ofproto_dpif *ofproto, struct flow *flow, struct dpif_flow_stats *stats, bool may_learn) { - struct ofport_dpif *in_port; struct xlate_in xin; - in_port = get_ofp_port(ofproto, flow->in_port.ofp_port); - if (in_port && in_port->is_tunnel) { - netdev_vport_inc_rx(in_port->up.netdev, stats); - } - xlate_in_init(&xin, ofproto, flow, NULL, stats->tcp_flags, NULL); xin.resubmit_stats = stats; xin.may_learn = may_learn; @@ -4253,18 +4186,12 @@ facet_push_stats(struct facet *facet, bool may_learn) facet->prev_packet_count = facet->packet_count; facet->prev_byte_count = facet->byte_count; facet->prev_used = facet->used; - - netflow_flow_update_time(facet->ofproto->netflow, &facet->nf_flow, - facet->used); - netflow_flow_update_flags(&facet->nf_flow, facet->tcp_flags); - mirror_update_stats(facet->ofproto->mbridge, facet->xout.mirrors, - stats.n_packets, stats.n_bytes); flow_push_stats(facet->ofproto, &facet->flow, &stats, may_learn); } } static void -push_all_stats__(bool run_fast) +push_all_stats(void) { static long long int rl = LLONG_MIN; struct ofproto_dpif *ofproto; @@ -4281,9 +4208,6 @@ push_all_stats__(bool run_fast) cls_cursor_init(&cursor, &ofproto->facets, NULL); CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { facet_push_stats(facet, false); - if (run_fast) { - run_fast_rl(); - } } ovs_rwlock_unlock(&ofproto->facets.rwlock); } @@ -4291,12 +4215,6 @@ push_all_stats__(bool run_fast) rl = time_msec() + 100; } -static void -push_all_stats(void) -{ - push_all_stats__(true); -} - void rule_dpif_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats) @@ -4309,9 +4227,15 @@ rule_dpif_credit_stats(struct rule_dpif *rule, } bool -rule_dpif_fail_open(const struct rule_dpif *rule) +rule_dpif_is_fail_open(const struct rule_dpif *rule) { - return rule->up.cr.priority == FAIL_OPEN_PRIORITY; + return is_fail_open_rule(&rule->up); +} + +bool +rule_dpif_is_table_miss(const struct rule_dpif *rule) +{ + return rule_is_table_miss(&rule->up); } ovs_be64 @@ -4356,37 +4280,20 @@ subfacet_find(struct dpif_backer *backer, const struct nlattr *key, return NULL; } -/* Searches 'facet' (within 'ofproto') for a subfacet with the specified - * 'key_fitness', 'key', and 'key_len' members in 'miss'. Returns the - * existing subfacet if there is one, otherwise creates and returns a - * new subfacet. */ +/* Creates and returns a new subfacet within 'facet' for the flow in 'miss'. + * 'key_hash' must be a hash over miss->key. The caller must have already + * ensured that no subfacet subfacet already exists. */ static struct subfacet * -subfacet_create(struct facet *facet, struct flow_miss *miss) +subfacet_create(struct facet *facet, struct flow_miss *miss, uint32_t key_hash) { struct dpif_backer *backer = miss->ofproto->backer; const struct nlattr *key = miss->key; size_t key_len = miss->key_len; - uint32_t key_hash; struct subfacet *subfacet; - key_hash = odp_flow_key_hash(key, key_len); - - if (list_is_empty(&facet->subfacets)) { - subfacet = &facet->one_subfacet; - } else { - subfacet = subfacet_find(backer, key, key_len, key_hash); - if (subfacet) { - if (subfacet->facet == facet) { - return subfacet; - } - - /* This shouldn't happen. */ - VLOG_ERR_RL(&rl, "subfacet with wrong facet"); - subfacet_destroy(subfacet); - } - - subfacet = xmalloc(sizeof *subfacet); - } + subfacet = (list_is_empty(&facet->subfacets) + ? &facet->one_subfacet + : xmalloc(sizeof *subfacet)); COVERAGE_INC(subfacet_create); hmap_insert(&backer->subfacets, &subfacet->hmap_node, key_hash); @@ -4458,7 +4365,6 @@ subfacet_destroy_batch(struct dpif_backer *backer, subfacet_reset_dp_stats(subfacets[i], &stats[i]); subfacets[i]->path = SF_NOT_INSTALLED; subfacet_destroy(subfacets[i]); - run_fast_rl(); } } @@ -4633,9 +4539,7 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc); } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) { cls_rule = &ofproto->drop_frags_rule->up.cr; - if (wc) { - flow_wildcards_init_exact(wc); - } + /* Frag mask in wc already set above. */ } else { cls_rule = classifier_lookup(cls, flow, wc); } @@ -4743,11 +4647,7 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes) { struct rule_dpif *rule = rule_dpif_cast(rule_); - /* push_all_stats() can handle flow misses which, when using the learn - * action, can cause rules to be added and deleted. This can corrupt our - * caller's datastructures which assume that rule_get_stats() doesn't have - * an impact on the flow table. To be safe, we disable miss handling. */ - push_all_stats__(false); + push_all_stats(); /* Start from historical data for 'rule' itself that are no longer tracked * in facets. This counts, for example, facets that have expired. */ @@ -4790,6 +4690,137 @@ rule_modify_actions(struct rule *rule_, bool reset_counters) complete_operation(rule); } + +static struct group_dpif *group_dpif_cast(const struct ofgroup *group) +{ + return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL; +} + +static struct ofgroup * +group_alloc(void) +{ + struct group_dpif *group = xzalloc(sizeof *group); + return &group->up; +} + +static void +group_dealloc(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + free(group); +} + +static void +group_construct_stats(struct group_dpif *group) + OVS_REQUIRES(group->stats_mutex) +{ + group->packet_count = 0; + group->byte_count = 0; + if (!group->bucket_stats) { + group->bucket_stats = xcalloc(group->up.n_buckets, + sizeof *group->bucket_stats); + } else { + memset(group->bucket_stats, 0, group->up.n_buckets * + sizeof *group->bucket_stats); + } +} + +static enum ofperr +group_construct(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + ovs_mutex_init(&group->stats_mutex); + ovs_mutex_lock(&group->stats_mutex); + group_construct_stats(group); + ovs_mutex_unlock(&group->stats_mutex); + return 0; +} + +static void +group_destruct__(struct group_dpif *group) + OVS_REQUIRES(group->stats_mutex) +{ + free(group->bucket_stats); + group->bucket_stats = NULL; +} + +static void +group_destruct(struct ofgroup *group_) +{ + struct group_dpif *group = group_dpif_cast(group_); + ovs_mutex_lock(&group->stats_mutex); + group_destruct__(group); + ovs_mutex_unlock(&group->stats_mutex); + ovs_mutex_destroy(&group->stats_mutex); +} + +static enum ofperr +group_modify(struct ofgroup *group_, struct ofgroup *victim_) +{ + struct group_dpif *group = group_dpif_cast(group_); + struct group_dpif *victim = group_dpif_cast(victim_); + + ovs_mutex_lock(&group->stats_mutex); + if (victim->up.n_buckets < group->up.n_buckets) { + group_destruct__(group); + } + group_construct_stats(group); + ovs_mutex_unlock(&group->stats_mutex); + + return 0; +} + +static enum ofperr +group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs) +{ + struct group_dpif *group = group_dpif_cast(group_); + + /* Start from historical data for 'group' itself that are no longer tracked + * in facets. This counts, for example, facets that have expired. */ + ovs_mutex_lock(&group->stats_mutex); + ogs->packet_count = group->packet_count; + ogs->byte_count = group->byte_count; + memcpy(ogs->bucket_stats, group->bucket_stats, + group->up.n_buckets * sizeof *group->bucket_stats); + ovs_mutex_unlock(&group->stats_mutex); + + return 0; +} + +bool +group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id, + struct group_dpif **group) + OVS_TRY_RDLOCK(true, (*group)->up.rwlock) +{ + struct ofgroup *ofgroup; + bool found; + + *group = NULL; + found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup); + *group = found ? group_dpif_cast(ofgroup) : NULL; + + return found; +} + +void +group_dpif_release(struct group_dpif *group) + OVS_RELEASES(group->up.rwlock) +{ + ofproto_group_release(&group->up); +} + +void +group_dpif_get_buckets(const struct group_dpif *group, + const struct list **buckets) +{ + *buckets = &group->up.buckets; +} + +enum ofp11_group_type +group_dpif_get_type(const struct group_dpif *group) +{ + return group->up.type; +} /* Sends 'packet' out 'ofport'. * May modify 'packet'. @@ -4890,7 +4921,7 @@ set_netflow(struct ofproto *ofproto_, return netflow_set_options(ofproto->netflow, netflow_options); } else if (ofproto->netflow) { ofproto->backer->need_revalidate = REV_RECONFIGURE; - netflow_destroy(ofproto->netflow); + netflow_unref(ofproto->netflow); ofproto->netflow = NULL; } @@ -4905,46 +4936,6 @@ get_netflow_ids(const struct ofproto *ofproto_, dpif_get_netflow_ids(ofproto->backer->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->path == SF_FAST_PATH) { - struct dpif_flow_stats stats; - - subfacet_install(subfacet, &facet->xout.odp_actions, - &stats); - subfacet_update_stats(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 cls_cursor cursor; - struct facet *facet; - - ovs_rwlock_rdlock(&ofproto->facets.rwlock); - cls_cursor_init(&cursor, &ofproto->facets, NULL); - CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { - send_active_timeout(ofproto, facet); - } - ovs_rwlock_unlock(&ofproto->facets.rwlock); -} static struct ofproto_dpif * ofproto_dpif_lookup(const char *name) @@ -5055,7 +5046,7 @@ trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule) actions = rule_dpif_get_actions(rule); ds_put_char_multiple(result, '\t', level); - ds_put_cstr(result, "OpenFlow "); + ds_put_cstr(result, "OpenFlow actions="); ofpacts_format(actions->ofpacts, actions->ofpacts_len, result); ds_put_char(result, '\n'); @@ -5086,7 +5077,7 @@ trace_format_regs(struct ds *result, int level, const char *title, ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s:", title); for (i = 0; i < FLOW_N_REGS; i++) { - ds_put_format(result, " reg%zu=0x%"PRIx32, i, trace->flow.regs[i]); + ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]); } ds_put_char(result, '\n'); } @@ -5127,37 +5118,39 @@ trace_report(struct xlate_in *xin, const char *s, int recurse) ds_put_char(result, '\n'); } -static void -ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], - void *aux OVS_UNUSED) +/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following + * forms are supported: + * + * - [dpname] odp_flow [-generate | packet] + * - bridge br_flow [-generate | packet] + * + * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure + * returns a nonnull error message. */ +static const char * +parse_flow_and_packet(int argc, const char *argv[], + struct ofproto_dpif **ofprotop, struct flow *flow, + struct ofpbuf **packetp) { const struct dpif_backer *backer = NULL; - struct ofproto_dpif *ofproto; - struct ofpbuf odp_key, odp_mask; + const char *error = NULL; + struct simap port_names = SIMAP_INITIALIZER(&port_names); struct ofpbuf *packet; - struct ds result; - struct flow flow; - struct simap port_names; - char *s; + struct ofpbuf odp_key; + struct ofpbuf odp_mask; - packet = NULL; - backer = NULL; - ds_init(&result); ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); - simap_init(&port_names); /* Handle "-generate" or a hex string as the last argument. */ if (!strcmp(argv[argc - 1], "-generate")) { packet = ofpbuf_new(0); argc--; } else { - const char *error = eth_from_hex(argv[argc - 1], &packet); + error = eth_from_hex(argv[argc - 1], &packet); if (!error) { argc--; } else if (argc == 4) { /* The 3-argument form must end in "-generate' or a hex string. */ - unixctl_command_reply_error(conn, error); goto exit; } } @@ -5174,12 +5167,15 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], dp_type = argv[1]; } backer = shash_find_data(&all_dpif_backers, dp_type); - } else { + } else if (argc == 2) { struct shash_node *node; if (shash_count(&all_dpif_backers) == 1) { node = shash_first(&all_dpif_backers); backer = node->data; } + } else { + error = "Syntax error"; + goto exit; } if (backer && backer->dpif) { struct dpif_port dpif_port; @@ -5194,96 +5190,220 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], * bridge is specified. If function odp_flow_key_from_string() * returns 0, the flow is a odp_flow. If function * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */ - if (!odp_flow_from_string(argv[argc - 1], &port_names, &odp_key, &odp_mask)) { + if (!odp_flow_from_string(argv[argc - 1], &port_names, + &odp_key, &odp_mask)) { if (!backer) { - unixctl_command_reply_error(conn, "Cannot find the datapath"); + error = "Cannot find the datapath"; goto exit; } - if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, &flow, - NULL, &ofproto, NULL)) { - unixctl_command_reply_error(conn, "Invalid datapath flow"); + if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow, + NULL, ofprotop, NULL, NULL, NULL, NULL)) { + error = "Invalid datapath flow"; goto exit; } - ds_put_format(&result, "Bridge: %s\n", ofproto->up.name); - } else if (!parse_ofp_exact_flow(&flow, argv[argc - 1])) { + } else if (!parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL)) { if (argc != 3) { - unixctl_command_reply_error(conn, "Must specify bridge name"); + error = "Must specify bridge name"; goto exit; } - ofproto = ofproto_dpif_lookup(argv[1]); - if (!ofproto) { - unixctl_command_reply_error(conn, "Unknown bridge name"); + *ofprotop = ofproto_dpif_lookup(argv[1]); + if (!*ofprotop) { + error = "Unknown bridge name"; goto exit; } } else { - unixctl_command_reply_error(conn, "Bad flow syntax"); + error = "Bad flow syntax"; goto exit; } /* Generate a packet, if requested. */ if (packet) { if (!packet->size) { - flow_compose(packet, &flow); + flow_compose(packet, flow); } else { - union flow_in_port in_port_; - - in_port_ = flow.in_port; - ds_put_cstr(&result, "Packet: "); - s = ofp_packet_to_string(packet->data, packet->size); - ds_put_cstr(&result, s); - free(s); + union flow_in_port in_port = flow->in_port; /* Use the metadata from the flow and the packet argument * to reconstruct the flow. */ - flow_extract(packet, flow.skb_priority, flow.pkt_mark, NULL, - &in_port_, &flow); + flow_extract(packet, flow->skb_priority, flow->pkt_mark, NULL, + &in_port, flow); } } - ofproto_trace(ofproto, &flow, packet, &result); - unixctl_command_reply(conn, ds_cstr(&result)); + error = NULL; exit: - ds_destroy(&result); - ofpbuf_delete(packet); + if (error) { + ofpbuf_delete(packet); + packet = NULL; + } + *packetp = packet; ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); simap_destroy(&port_names); + return error; +} + +static void +ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], + void *aux OVS_UNUSED) +{ + struct ofproto_dpif *ofproto; + struct ofpbuf *packet; + const char *error; + struct flow flow; + + error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); + if (!error) { + struct ds result; + + ds_init(&result); + ofproto_trace(ofproto, &flow, packet, NULL, 0, &result); + unixctl_command_reply(conn, ds_cstr(&result)); + ds_destroy(&result); + ofpbuf_delete(packet); + } else { + unixctl_command_reply_error(conn, error); + } } +static void +ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + enum ofputil_protocol usable_protocols; + struct ofproto_dpif *ofproto; + bool enforce_consistency; + struct ofpbuf ofpacts; + struct ofpbuf *packet; + struct ds result; + struct flow flow; + uint16_t in_port; + + /* Three kinds of error return values! */ + enum ofperr retval; + const char *error; + char *rw_error; + + packet = NULL; + ds_init(&result); + ofpbuf_init(&ofpacts, 0); + + /* Parse actions. */ + rw_error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols); + if (rw_error) { + unixctl_command_reply_error(conn, rw_error); + free(rw_error); + goto exit; + } + + /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of + * consistency between the flow and the actions. With -consistent, we + * enforce consistency even for a flow supported in OpenFlow 1.0. */ + if (!strcmp(argv[1], "-consistent")) { + enforce_consistency = true; + argv++; + argc--; + } else { + enforce_consistency = false; + } + + error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); + if (error) { + unixctl_command_reply_error(conn, error); + goto exit; + } + + /* Do the same checks as handle_packet_out() in ofproto.c. + * + * We pass a 'table_id' of 0 to ofproto_check_ofpacts(), which isn't + * strictly correct because these actions aren't in any table, but it's OK + * because it 'table_id' is used only to check goto_table instructions, but + * packet-outs take a list of actions and therefore it can't include + * instructions. + * + * We skip the "meter" check here because meter is an instruction, not an + * action, and thus cannot appear in ofpacts. */ + in_port = ofp_to_u16(flow.in_port.ofp_port); + if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) { + unixctl_command_reply_error(conn, "invalid in_port"); + goto exit; + } + if (enforce_consistency) { + retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow, + u16_to_ofp(ofproto->up.max_ports), + 0, 0, usable_protocols); + } else { + retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow, + u16_to_ofp(ofproto->up.max_ports), 0, 0, + &usable_protocols); + } + + if (retval) { + ds_clear(&result); + ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval)); + unixctl_command_reply_error(conn, ds_cstr(&result)); + goto exit; + } + + ofproto_trace(ofproto, &flow, packet, ofpacts.data, ofpacts.size, &result); + unixctl_command_reply(conn, ds_cstr(&result)); + +exit: + ds_destroy(&result); + ofpbuf_delete(packet); + ofpbuf_uninit(&ofpacts); +} + +/* Implements a "trace" through 'ofproto''s flow table, appending a textual + * description of the results to 'ds'. + * + * The trace follows a packet with the specified 'flow' through the flow + * table. 'packet' may be nonnull to trace an actual packet, with consequent + * side effects (if it is nonnull then its flow must be 'flow'). + * + * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to + * trace, otherwise the actions are determined by a flow table lookup. */ static void ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, - const struct ofpbuf *packet, struct ds *ds) + const struct ofpbuf *packet, + const struct ofpact ofpacts[], size_t ofpacts_len, + struct ds *ds) { struct rule_dpif *rule; struct flow_wildcards wc; + ds_put_format(ds, "Bridge: %s\n", ofproto->up.name); ds_put_cstr(ds, "Flow: "); flow_format(ds, flow); ds_put_char(ds, '\n'); flow_wildcards_init_catchall(&wc); - rule_dpif_lookup(ofproto, flow, &wc, &rule); + if (ofpacts) { + rule = NULL; + } else { + rule_dpif_lookup(ofproto, flow, &wc, &rule); - trace_format_rule(ds, 0, rule); - if (rule == ofproto->miss_rule) { - ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n"); - } else if (rule == ofproto->no_packet_in_rule) { - ds_put_cstr(ds, "\nNo match, packets dropped because " - "OFPPC_NO_PACKET_IN is set on in_port.\n"); - } else if (rule == ofproto->drop_frags_rule) { - ds_put_cstr(ds, "\nPackets dropped because they are IP fragments " - "and the fragment handling mode is \"drop\".\n"); + trace_format_rule(ds, 0, rule); + if (rule == ofproto->miss_rule) { + ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n"); + } else if (rule == ofproto->no_packet_in_rule) { + ds_put_cstr(ds, "\nNo match, packets dropped because " + "OFPPC_NO_PACKET_IN is set on in_port.\n"); + } else if (rule == ofproto->drop_frags_rule) { + ds_put_cstr(ds, "\nPackets dropped because they are IP fragments " + "and the fragment handling mode is \"drop\".\n"); + } } - if (rule) { + if (rule || ofpacts) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions; struct trace_ctx trace; struct match match; - uint8_t tcp_flags; + uint16_t tcp_flags; tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0; trace.result = ds; @@ -5291,6 +5411,10 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub); xlate_in_init(&trace.xin, ofproto, flow, rule, tcp_flags, packet); + if (ofpacts) { + trace.xin.ofpacts = ofpacts; + trace.xin.ofpacts_len = ofpacts_len; + } trace.xin.resubmit_hook = trace_resubmit; trace.xin.report_hook = trace_report; @@ -5446,7 +5570,7 @@ dpif_show_backer(const struct dpif_backer *backer, struct ds *ds) ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n", dpif_name(backer->dpif), n_hit, n_missed); - ds_put_format(ds, "\tflows: cur: %zu, avg: %u, max: %u," + ds_put_format(ds, "\tflows: cur: %"PRIuSIZE", avg: %u, max: %u," " life span: %lldms\n", hmap_count(&backer->subfacets), backer->avg_n_subfacet, backer->max_n_subfacet, backer->avg_subfacet_life); @@ -5557,7 +5681,7 @@ ofproto_unixctl_dpif_dump_megaflows(struct unixctl_conn *conn, CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { cls_rule_format(&facet->cr, &ds); ds_put_cstr(&ds, ", "); - ds_put_format(&ds, "n_subfacets:%zu, ", list_size(&facet->subfacets)); + ds_put_format(&ds, "n_subfacets:%"PRIuSIZE", ", list_size(&facet->subfacets)); ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0); ds_put_cstr(&ds, "Datapath actions: "); if (facet->xout.slow) { @@ -5724,8 +5848,12 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register( "ofproto/trace", - "[dp_name]|bridge odp_flow|br_flow [-generate|packet]", + "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]", 1, 3, ofproto_unixctl_trace, NULL); + unixctl_command_register( + "ofproto/trace-packet-out", + "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions", + 2, 6, ofproto_unixctl_trace_actions, NULL); unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, @@ -6010,14 +6138,12 @@ const struct ofproto_class ofproto_dpif_class = { del, port_open_type, type_run, - type_run_fast, type_wait, alloc, construct, destruct, dealloc, run, - run_fast, wait, get_memory_usage, flush, @@ -6063,6 +6189,7 @@ const struct ofproto_class ofproto_dpif_class = { get_stp_status, set_stp_port, get_stp_port_status, + get_stp_port_stats, set_queues, bundle_set, bundle_remove, @@ -6077,10 +6204,10 @@ const struct ofproto_class ofproto_dpif_class = { NULL, /* meter_set */ NULL, /* meter_get */ NULL, /* meter_del */ - NULL, /* group_alloc */ - NULL, /* group_construct */ - NULL, /* group_destruct */ - NULL, /* group_dealloc */ - NULL, /* group_modify */ - NULL, /* group_get_stats */ + group_alloc, /* group_alloc */ + group_construct, /* group_construct */ + group_destruct, /* group_destruct */ + group_dealloc, /* group_dealloc */ + group_modify, /* group_modify */ + group_get_stats, /* group_get_stats */ };