This patch adds more flow related stats to the output of "ovs-appctl dpif/show". Specifically, the follow information are added per ofproto:
- Max flow table size - Average flow table size - Average flow table add rate - Average flow table delete rate - Average flow entry life in milliseconds Feature #15366 Signed-off-by: Andy Zhou <az...@nicira.com> --- ofproto/ofproto-dpif.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++- tests/ofproto-dpif.at | 44 +++++++++++-- tests/tunnel.at | 39 ++++++++---- 3 files changed, 224 insertions(+), 20 deletions(-) diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index d1b9f34..001cf37 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -370,6 +370,7 @@ struct subfacet { int key_len; long long int used; /* Time last used; time created if not used. */ + long long int created; /* Time created. */ uint64_t dp_packet_count; /* Last known packet count in the datapath. */ uint64_t dp_byte_count; /* Last known byte count in the datapath. */ @@ -444,6 +445,7 @@ struct facet { /* Owned data. */ struct list subfacets; long long int used; /* Time last used; time created if not used. */ + long long int created; /* Time created. */ /* Key. */ struct flow flow; @@ -667,6 +669,13 @@ odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port); static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, uint64_t delta); +struct avg_flow_rates { + float flow_add; /* Moving average of new flows created per minute. */ + float flow_del; /* Moving average of flows deleted per minute. */ +}; +static void show_dp_rates(struct ds *ds, const char *heading, + const struct avg_flow_rates *rates); +static void exp_mavg(float *avg, int base, float new); struct ofproto_dpif { struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */ @@ -721,7 +730,34 @@ struct ofproto_dpif { /* Per ofproto's dpif stats. */ uint64_t n_hit; uint64_t n_missed; + + /* Keep track of maximum number of flows */ + uint32_t max_n_flows; + + long long int created; /* Time when it is created. */ + + struct avg_flow_rates hourly; + struct avg_flow_rates daily; + long long int last_minute; /* Maintain last minute time stamp for + computing moving averages. */ + /* Keep track of the total number of flow added and deleted and + * flow life span, Useful for computing the flow rates stats exposed + * via "ovs-appctl dpif/show". + */ + uint32_t flow_add_count; /* Counters for the last minute. */ + uint32_t flow_del_count; + uint64_t total_flow_add_count; /* Total counters exclude current minute. */ + uint64_t total_flow_del_count; + uint64_t total_flow_life_span; /* In milliseconds. */ + uint64_t total_flow_count; /* Sum of per second flow counts. */ }; +static uint64_t compute_avg_flow_life_span(const struct ofproto_dpif *ofproto); +static float compute_avg_flow_count(const struct ofproto_dpif *ofproto, + int min); +static void update_moving_averages(struct ofproto_dpif *ofproto); +static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, + uint64_t delta); +static void update_max_flow_count(struct ofproto_dpif *ofproto); /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only * for debugging the asynchronous flow_mod implementation.) */ @@ -1301,6 +1337,18 @@ construct(struct ofproto *ofproto_) ofproto->n_hit = 0; ofproto->n_missed = 0; + ofproto->max_n_flows = 0; + ofproto->created = time_msec(); + ofproto->last_minute = ofproto->created; + memset(&ofproto->hourly, 0, sizeof ofproto->hourly); + memset(&ofproto->daily, 0, sizeof ofproto->daily); + ofproto->flow_add_count = 0; + ofproto->flow_del_count = 0; + ofproto->total_flow_add_count = 0; + ofproto->total_flow_del_count = 0; + ofproto->total_flow_life_span = 0; + ofproto->total_flow_count = 0; + return error; } @@ -4026,6 +4074,9 @@ expire(struct dpif_backer *backer) continue; } + /* Keep track of the max number of flows per ofproto_dpif. */ + update_max_flow_count(ofproto); + /* Expire subfacets that have been idle too long. */ dp_max_idle = subfacet_max_idle(ofproto); expire_subfacets(ofproto, dp_max_idle); @@ -4146,6 +4197,9 @@ update_stats(struct dpif_backer *backer) continue; } + ofproto->total_flow_count += hmap_count(&ofproto->subfacets); + update_moving_averages(ofproto); + ofport = get_ofp_port(ofproto, flow.in_port); if (ofport && ofport->tnl_port) { netdev_vport_inc_rx(ofport->up.netdev, stats); @@ -5044,6 +5098,7 @@ subfacet_create(struct facet *facet, struct flow_miss *miss, subfacet->key = xmemdup(key, key_len); subfacet->key_len = key_len; subfacet->used = now; + subfacet->created = now; subfacet->dp_packet_count = 0; subfacet->dp_byte_count = 0; subfacet->actions_len = 0; @@ -5055,6 +5110,7 @@ subfacet_create(struct facet *facet, struct flow_miss *miss, subfacet->initial_vals = miss->initial_vals; subfacet->odp_in_port = miss->odp_in_port; + ofproto->flow_add_count++; return subfacet; } @@ -5066,6 +5122,10 @@ subfacet_destroy__(struct subfacet *subfacet) struct facet *facet = subfacet->facet; struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto); + /* Update ofproto stats before uninstall the subfacet. */ + ofproto->flow_del_count++; + ofproto->total_flow_life_span += (time_msec() - subfacet->created); + subfacet_uninstall(subfacet); hmap_remove(&ofproto->subfacets, &subfacet->hmap_node); list_remove(&subfacet->list_node); @@ -8064,14 +8124,37 @@ show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds) { const struct shash_node **ports; int i; + struct avg_flow_rates lifetime; + long long int minutes; + const int min_ms = 60 * 1000; /* milliseconds in one minute. */ + + minutes = (time_msec() - ofproto->created) / min_ms; + if (minutes > 0) { + lifetime.flow_add = (float)ofproto->total_flow_add_count / minutes; + lifetime.flow_del = (float)ofproto->total_flow_del_count / minutes; + }else { + lifetime.flow_add = 0.0; + lifetime.flow_del = 0.0; + } ds_put_format(ds, "%s (%s):\n", ofproto->up.name, dpif_name(ofproto->backer->dpif)); ds_put_format(ds, "\tlookups: hit:%"PRIu64" missed:%"PRIu64"\n", ofproto->n_hit, ofproto->n_missed); - ds_put_format(ds, "\tflows: %zu\n", - hmap_count(&ofproto->subfacets)); + ds_put_format(ds, "\tflows: cur: %zu, avg: %5.3f, max: %d," + " life span: %zu(ms)\n", + hmap_count(&ofproto->subfacets), + compute_avg_flow_count(ofproto, minutes), + ofproto->max_n_flows, + compute_avg_flow_life_span(ofproto)); + if (minutes >= 60) { + show_dp_rates(ds, "\t\thourly avg:", &ofproto->hourly); + } + if (minutes >= 60 * 24) { + show_dp_rates(ds, "\t\tdaily avg:", &ofproto->daily); + } + show_dp_rates(ds, "\t\toverall avg:", &lifetime); ports = shash_sort(&ofproto->up.port_by_name); for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) { @@ -8488,6 +8571,80 @@ odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port) return OFPP_NONE; } } +static uint64_t +compute_avg_flow_life_span(const struct ofproto_dpif *ofproto) +{ + if (ofproto-> total_flow_del_count + ofproto->flow_del_count) { + return (ofproto->total_flow_life_span / + (ofproto->total_flow_del_count + ofproto->flow_del_count)); + } + return 0; +} + +static float +compute_avg_flow_count(const struct ofproto_dpif *ofproto, int min) +{ + float size = 0.0; + + if (min != 0) { + size = ofproto->total_flow_count /min/60.0; + } + + return size; +} + +static void +show_dp_rates(struct ds *ds, const char *heading, + const struct avg_flow_rates *rates) +{ + ds_put_cstr(ds, heading); + ds_put_cstr(ds, " "); + ds_put_format(ds, "add rate: %5.3f/min, del rate: %5.3f/min\n", + rates->flow_add, rates->flow_del); +} + +static void +update_max_flow_count(struct ofproto_dpif *ofproto) +{ + uint64_t c = hmap_count(&ofproto->subfacets); + + if (c > ofproto->max_n_flows) { + ofproto->max_n_flows = c; + } +} + +/* Compute exponentail moving average */ +static void +exp_mavg(float* avg, int base, float new) +{ + *avg = (*avg * (base - 1) + new) / base; +} + +static void +update_moving_averages(struct ofproto_dpif *ofproto) +{ + const int min_ms = 60 * 1000; /* milliseconds in one minute. */ + + /* Only update on the minute boundaries. */ + if (time_msec() - ofproto->last_minute >= min_ms) { + exp_mavg(&ofproto->hourly.flow_add, 60, + (float)ofproto->flow_add_count); + exp_mavg(&ofproto->hourly.flow_del, 60, + (float)ofproto->flow_del_count); + + /* Update daily average on the hour boundaries. */ + if ((ofproto->last_minute - ofproto->created) % min_ms == 60) { + exp_mavg(&ofproto->daily.flow_add, 24, ofproto->hourly.flow_add); + exp_mavg(&ofproto->daily.flow_del, 24, ofproto->hourly.flow_del); + } + + ofproto->total_flow_add_count += ofproto->flow_add_count; + ofproto->total_flow_del_count += ofproto->flow_del_count; + ofproto->flow_add_count = 0; + ofproto->flow_del_count = 0; + ofproto->last_minute += min_ms; + } +} static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, uint64_t delta) diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 354fdc9..d2c9c8d 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -1480,13 +1480,15 @@ ADD_OF_PORTS([br1], [3]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (dummy) p2 2/2: (dummy) br1 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br1 65534/101: (dummy) p3 3/3: (dummy) ]) @@ -1494,7 +1496,8 @@ br1 (dummy@ovs-dummy): AT_CHECK([ovs-appctl dpif/show br0], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (dummy) p2 2/2: (dummy) @@ -1587,13 +1590,15 @@ warped AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:9 missed:1 - flows: 1 + flows: cur: 1, avg: 0.000, max: 1, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p2 2/2: (dummy) pbr0 1/none: (patch: peer=pbr1) br1 (dummy@ovs-dummy): lookups: hit:4 missed:1 - flows: 1 + flows: cur: 1, avg: 0.000, max: 1, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br1 65534/101: (dummy) p3 3/3: (dummy) pbr1 1/none: (patch: peer=pbr0) @@ -1620,3 +1625,32 @@ OFPST_PORT reply (xid=0x4): 1 ports OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto-dpif - ovs-appctl dpif/show rates]) +OVS_VSWITCHD_START([set Bridge br0 fail-mode=secure]) +ADD_OF_PORTS([br0], 1, 2) + +AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2]) + +for i in $(seq 1 61); do + ovs-appctl netdev-dummy/receive br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' + ovs-appctl time/warp 10000 + ovs-appctl time/warp 50000 +done + +AT_CHECK([ovs-appctl time/warp 10000], [0], [warped +]) + +AT_CHECK([ovs-appctl dpif/show], [0], [dnl +br0 (dummy@ovs-dummy): + lookups: hit:0 missed:61 + flows: cur: 0, avg: 0.017, max: 1, life span: 10001(ms) + hourly avg: add rate: 0.641/min, del rate: 0.635/min + overall avg: add rate: 1.000/min, del rate: 0.984/min + br0 65534/100: (dummy) + p1 1/1: (dummy) + p2 2/2: (dummy) +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP diff --git a/tests/tunnel.at b/tests/tunnel.at index aee543e..78e43f5 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -17,7 +17,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1) @@ -43,7 +44,8 @@ AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \ AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1) @@ -81,7 +83,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: remote_ip=1.1.1.1) p2 2/2: (dummy) @@ -128,7 +131,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1) p2 2/2: (dummy) @@ -163,7 +167,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit) p2 2/2: (dummy) @@ -208,7 +213,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: key=flow, remote_ip=1.1.1.1) p2 2/1: (gre: key=flow, remote_ip=2.2.2.2) @@ -243,7 +249,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: key=1, remote_ip=1.1.1.1) p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1) @@ -298,7 +305,8 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (gre: key=flow, remote_ip=1.1.1.1) p2 2/1: (gre: key=3, remote_ip=3.3.3.3) @@ -337,7 +345,8 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (vxlan: remote_ip=1.1.1.1) ]) @@ -352,7 +361,8 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=lisp \ AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (lisp: remote_ip=1.1.1.1) ]) @@ -367,7 +377,8 @@ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (vxlan: dst_port=4341, remote_ip=1.1.1.1) ]) @@ -379,7 +390,8 @@ AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=5000]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/2: (vxlan: dst_port=5000, remote_ip=1.1.1.1) ]) @@ -391,7 +403,8 @@ AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=8472]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl br0 (dummy@ovs-dummy): lookups: hit:0 missed:0 - flows: 0 + flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms) + overall avg: add rate: 0.000/min, del rate: 0.000/min br0 65534/100: (dummy) p1 1/1: (vxlan: remote_ip=1.1.1.1) ]) -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev