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. */
static void facet_push_stats(struct facet *);
static void facet_learn(struct facet *);
static void facet_account(struct facet *);
+static void push_all_stats(void);
static struct subfacet *facet_get_subfacet(struct facet *);
static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto,
uint64_t delta);
+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. */
+};
+static void show_dp_rates(struct ds *ds, const char *heading,
+ const struct avg_subfacet_rates *rates);
+static void exp_mavg(double *avg, int base, double new);
struct ofproto_dpif {
struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
/* Per ofproto's dpif stats. */
uint64_t n_hit;
uint64_t n_missed;
+
+ /* Subfacet statistics.
+ *
+ * These keep track of the total number of subfacets added and deleted and
+ * flow life span. They are useful for computing the flow rates stats
+ * 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 int max_n_subfacet; /* Maximum number of flows */
+
+ /* The average number of subfacets... */
+ struct avg_subfacet_rates hourly; /* ...over the last hour. */
+ struct avg_subfacet_rates daily; /* ...over the last day. */
+ long long int last_minute; /* Last time 'hourly' was updated. */
+
+ /* Number of subfacets added or deleted since 'last_minute'. */
+ unsigned int subfacet_add_count;
+ unsigned int subfacet_del_count;
+
+ /* Number of subfacets added or deleted from 'created' to 'last_minute.' */
+ unsigned long long int total_subfacet_add_count;
+ unsigned long long int total_subfacet_del_count;
+
+ /* Sum of the number of milliseconds that each subfacet existed,
+ * over the subfacets that have been added and then later deleted. */
+ unsigned long long int total_subfacet_life_span;
+
+ /* Incremented by the number of currently existing subfacets, each
+ * time we pull statistics from the kernel. */
+ unsigned long long int total_subfacet_count;
+
+ /* Number of times we pull statistics from the kernel. */
+ unsigned long long int n_update_stats;
};
+static unsigned long long int avg_subfacet_life_span(
+ const struct ofproto_dpif *);
+static double avg_subfacet_count(const struct ofproto_dpif *ofproto);
+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_subfacet_count(struct ofproto_dpif *ofproto);
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
* for debugging the asynchronous flow_mod implementation.) */
ofproto->n_hit = 0;
ofproto->n_missed = 0;
+ ofproto->max_n_subfacet = 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->subfacet_add_count = 0;
+ ofproto->subfacet_del_count = 0;
+ ofproto->total_subfacet_add_count = 0;
+ ofproto->total_subfacet_del_count = 0;
+ ofproto->total_subfacet_life_span = 0;
+ ofproto->total_subfacet_count = 0;
+ ofproto->n_update_stats = 0;
+
return error;
}
return 0;
}
+ push_all_stats();
+
*packets = mirror->packet_count;
*bytes = mirror->byte_count;
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
int error;
+ push_all_stats();
+
error = netdev_get_stats(ofport->up.netdev, stats);
if (!error && ofport_->ofp_port == OFPP_LOCAL) {
continue;
}
+ /* Keep track of the max number of flows per ofproto_dpif. */
+ update_max_subfacet_count(ofproto);
+
/* Expire subfacets that have been idle too long. */
dp_max_idle = subfacet_max_idle(ofproto);
expire_subfacets(ofproto, dp_max_idle);
continue;
}
+ ofproto->total_subfacet_count += hmap_count(&ofproto->subfacets);
+ ofproto->n_update_stats++;
+ 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);
}
}
+static void
+push_all_stats(void)
+{
+ static long long int rl = LLONG_MIN;
+ struct ofproto_dpif *ofproto;
+
+ if (time_msec() < rl) {
+ return;
+ }
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct facet *facet;
+
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ facet_push_stats(facet);
+ }
+ }
+
+ rl = time_msec() + 100;
+}
+
static void
rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
{
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;
subfacet->initial_vals = miss->initial_vals;
subfacet->odp_in_port = miss->odp_in_port;
+ ofproto->subfacet_add_count++;
return 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->subfacet_del_count++;
+ ofproto->total_subfacet_life_span += (time_msec() - subfacet->created);
+
subfacet_uninstall(subfacet);
hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
list_remove(&subfacet->list_node);
static void
rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
{
- struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule_->ofproto);
struct rule_dpif *rule = rule_dpif_cast(rule_);
struct facet *facet;
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
- facet_push_stats(facet);
- }
+ 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. */
{
const struct shash_node **ports;
int i;
+ struct avg_subfacet_rates lifetime;
+ unsigned 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.add_rate = (double)ofproto->total_subfacet_add_count
+ / minutes;
+ lifetime.del_rate = (double)ofproto->total_subfacet_del_count
+ / minutes;
+ }else {
+ lifetime.add_rate = 0.0;
+ lifetime.del_rate = 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: %llu(ms)\n",
+ hmap_count(&ofproto->subfacets),
+ avg_subfacet_count(ofproto),
+ ofproto->max_n_subfacet,
+ avg_subfacet_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++) {
return OFPP_NONE;
}
}
+static unsigned long long int
+avg_subfacet_life_span(const struct ofproto_dpif *ofproto)
+{
+ unsigned long long int dc;
+ unsigned long long int avg;
+
+ dc = ofproto->total_subfacet_del_count + ofproto->subfacet_del_count;
+ avg = dc ? ofproto->total_subfacet_life_span / dc : 0;
+
+ return avg;
+}
+
+static double
+avg_subfacet_count(const struct ofproto_dpif *ofproto)
+{
+ double avg_c = 0.0;
+
+ if (ofproto->n_update_stats) {
+ avg_c = (double)ofproto->total_subfacet_count
+ / ofproto->n_update_stats;
+ }
+
+ return avg_c;
+}
+
+static void
+show_dp_rates(struct ds *ds, const char *heading,
+ const struct avg_subfacet_rates *rates)
+{
+ ds_put_format(ds, "%s add rate: %5.3f/min, del rate: %5.3f/min\n",
+ heading, rates->add_rate, rates->del_rate);
+}
+
+static void
+update_max_subfacet_count(struct ofproto_dpif *ofproto)
+{
+ ofproto->max_n_subfacet = MAX(ofproto->max_n_subfacet,
+ hmap_count(&ofproto->subfacets));
+}
+
+/* Compute exponentially weighted moving average, adding 'new' as the newest,
+ * most heavily weighted element. 'base' designates the rate of decay: after
+ * 'base' further updates, 'new''s weight in the EWMA decays to about 1/e
+ * (about .37). */
+static void
+exp_mavg(double *avg, int base, double 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. */
+
+ /* Update hourly averages on the minute boundaries. */
+ if (time_msec() - ofproto->last_minute >= min_ms) {
+ exp_mavg(&ofproto->hourly.add_rate, 60, ofproto->subfacet_add_count);
+ exp_mavg(&ofproto->hourly.del_rate, 60, ofproto->subfacet_del_count);
+
+ /* Update daily averages on the hour boundaries. */
+ if ((ofproto->last_minute - ofproto->created) / min_ms % 60 == 59) {
+ exp_mavg(&ofproto->daily.add_rate, 24, ofproto->hourly.add_rate);
+ exp_mavg(&ofproto->daily.del_rate, 24, ofproto->hourly.del_rate);
+ }
+
+ ofproto->total_subfacet_add_count += ofproto->subfacet_add_count;
+ ofproto->total_subfacet_del_count += ofproto->subfacet_del_count;
+ ofproto->subfacet_add_count = 0;
+ ofproto->subfacet_del_count = 0;
+ ofproto->last_minute += min_ms;
+ }
+}
static void
dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, uint64_t delta)