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

Reply via email to