#include "netlink.h"
#include "nx-match.h"
#include "odp-util.h"
+#include "odp-execute.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofp-actions.h"
struct hmap_node hmap_node; /* In struct ofproto_dpif 'subfacets' list. */
struct list list_node; /* In struct facet's 'facets' list. */
struct facet *facet; /* Owning facet. */
+ struct dpif_backer *backer; /* Owning backer. */
enum odp_key_fitness key_fitness;
struct nlattr *key;
static struct subfacet *subfacet_create(struct facet *, struct flow_miss *miss,
long long int now);
-static struct subfacet *subfacet_find(struct ofproto_dpif *,
+static struct subfacet *subfacet_find(struct dpif_backer *,
const struct nlattr *key, size_t key_len,
uint32_t key_hash);
static void subfacet_destroy(struct subfacet *);
static void subfacet_destroy__(struct subfacet *);
-static void subfacet_destroy_batch(struct ofproto_dpif *,
+static void subfacet_destroy_batch(struct dpif_backer *,
struct subfacet **, int n);
static void subfacet_reset_dp_stats(struct subfacet *,
struct dpif_flow_stats *);
int vid;
};
-static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
- uint32_t realdev, ovs_be16 vlan_tci);
+static uint16_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
+ uint16_t realdev_ofp_port,
+ ovs_be16 vlan_tci);
static bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *);
static void vsp_remove(struct ofport_dpif *);
static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid);
size_t key_len;
};
+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;
struct hmap drop_keys; /* Set of dropped odp keys. */
bool recv_set_enable; /* Enables or disables receiving packets. */
+
+ struct hmap subfacets;
+ struct governor *governor;
+
+ /* 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 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. */
+
+ /* The average number of subfacets... */
+ struct avg_subfacet_rates hourly; /* ...over the last hour. */
+ struct avg_subfacet_rates daily; /* ...over the last day. */
+ struct avg_subfacet_rates lifetime; /* ...over the switch lifetime. */
+ long long int last_minute; /* Last time 'hourly' was updated. */
+
+ /* Number of subfacets added or deleted since 'last_minute'. */
+ unsigned subfacet_add_count;
+ unsigned 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;
};
/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
static void drop_key_clear(struct dpif_backer *);
static struct ofport_dpif *
odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
-
-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);
+static void update_moving_averages(struct dpif_backer *backer);
struct ofproto_dpif {
struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
/* Special OpenFlow rules. */
struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
+ struct rule_dpif *drop_frags_rule; /* Used in OFPC_FRAG_DROP mode. */
/* Bridging. */
struct netflow *netflow;
/* Facets. */
struct hmap facets;
- struct hmap subfacets;
- struct governor *governor;
long long int consistency_rl;
/* Revalidation. */
/* 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 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.) */
}
HMAP_FOR_EACH (iter, up.hmap_node, &ofproto->up.ports) {
+ char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
const char *dp_port;
if (!iter->tnl_port) {
continue;
}
- dp_port = netdev_vport_get_dpif_port(iter->up.netdev);
+ dp_port = netdev_vport_get_dpif_port(iter->up.netdev,
+ namebuf, sizeof namebuf);
node = simap_find(&tmp_backers, dp_port);
if (node) {
simap_put(&backer->tnl_backers, dp_port, node->data);
}
}
+ if (backer->governor) {
+ size_t n_subfacets;
+
+ governor_run(backer->governor);
+
+ /* If the governor has shrunk to its minimum size and the number of
+ * subfacets has dwindled, then drop the governor entirely.
+ *
+ * For hysteresis, the number of subfacets to drop the governor is
+ * smaller than the number needed to trigger its creation. */
+ n_subfacets = hmap_count(&backer->subfacets);
+ if (n_subfacets * 4 < flow_eviction_threshold
+ && governor_is_idle(backer->governor)) {
+ governor_destroy(backer->governor);
+ backer->governor = NULL;
+ }
+ }
+
return 0;
}
return;
}
+ if (backer->governor) {
+ governor_wait(backer->governor);
+ }
+
timer_wait(&backer->next_expiration);
}
\f
shash_delete(&all_dpif_backers, node);
dpif_close(backer->dpif);
+ ovs_assert(hmap_is_empty(&backer->subfacets));
+ hmap_destroy(&backer->subfacets);
+ governor_destroy(backer->governor);
+
free(backer);
}
}
backer->type = xstrdup(type);
+ backer->governor = NULL;
backer->refcount = 1;
hmap_init(&backer->odp_to_ofport_map);
hmap_init(&backer->drop_keys);
+ hmap_init(&backer->subfacets);
timer_set_duration(&backer->next_expiration, 1000);
backer->need_revalidate = 0;
simap_init(&backer->tnl_backers);
return error;
}
+ backer->max_n_subfacet = 0;
+ backer->created = time_msec();
+ backer->last_minute = backer->created;
+ memset(&backer->hourly, 0, sizeof backer->hourly);
+ memset(&backer->daily, 0, sizeof backer->daily);
+ memset(&backer->lifetime, 0, sizeof backer->lifetime);
+ backer->subfacet_add_count = 0;
+ backer->subfacet_del_count = 0;
+ backer->total_subfacet_add_count = 0;
+ backer->total_subfacet_del_count = 0;
+ backer->avg_n_subfacet = 0;
+ backer->avg_subfacet_life = 0;
+
return error;
}
ofproto->has_bonded_bundles = false;
hmap_init(&ofproto->facets);
- hmap_init(&ofproto->subfacets);
- ofproto->governor = NULL;
ofproto->consistency_rl = LLONG_MIN;
for (i = 0; i < N_TABLES; i++) {
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;
}
ofpbuf_clear(&ofpacts);
error = add_internal_flow(ofproto, id++, &ofpacts,
&ofproto->no_packet_in_rule);
+ if (error) {
+ return error;
+ }
+
+ error = add_internal_flow(ofproto, id++, &ofpacts,
+ &ofproto->drop_frags_rule);
return error;
}
mac_learning_destroy(ofproto->ml);
hmap_destroy(&ofproto->facets);
- hmap_destroy(&ofproto->subfacets);
- governor_destroy(ofproto->governor);
hmap_destroy(&ofproto->vlandev_map);
hmap_destroy(&ofproto->realdev_vid_map);
}
}
- if (ofproto->governor) {
- size_t n_subfacets;
-
- governor_run(ofproto->governor);
-
- /* If the governor has shrunk to its minimum size and the number of
- * subfacets has dwindled, then drop the governor entirely.
- *
- * For hysteresis, the number of subfacets to drop the governor is
- * smaller than the number needed to trigger its creation. */
- n_subfacets = hmap_count(&ofproto->subfacets);
- if (n_subfacets * 4 < ofproto->up.flow_eviction_threshold
- && governor_is_idle(ofproto->governor)) {
- governor_destroy(ofproto->governor);
- ofproto->governor = NULL;
- }
- }
-
return 0;
}
VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
poll_immediate_wake();
}
- if (ofproto->governor) {
- governor_wait(ofproto->governor);
- }
}
static void
get_memory_usage(const struct ofproto *ofproto_, struct simap *usage)
{
const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ size_t n_subfacets = 0;
+ struct facet *facet;
simap_increase(usage, "facets", hmap_count(&ofproto->facets));
- simap_increase(usage, "subfacets", hmap_count(&ofproto->subfacets));
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ n_subfacets += list_size(&facet->subfacets);
+ }
+ simap_increase(usage, "subfacets", n_subfacets);
}
static void
n_batch = 0;
HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
- &ofproto->subfacets) {
+ &ofproto->backer->subfacets) {
+ if (ofproto_dpif_cast(subfacet->facet->rule->up.ofproto) != ofproto) {
+ continue;
+ }
+
if (subfacet->path != SF_NOT_INSTALLED) {
batch[n_batch++] = subfacet;
if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
- subfacet_destroy_batch(ofproto, batch, n_batch);
+ subfacet_destroy_batch(ofproto->backer, batch, n_batch);
n_batch = 0;
}
} else {
}
if (n_batch > 0) {
- subfacet_destroy_batch(ofproto, batch, n_batch);
+ subfacet_destroy_batch(ofproto->backer, batch, n_batch);
}
}
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct dpif_dp_stats s;
- uint64_t n_miss, n_no_pkt_in, n_bytes;
+ uint64_t n_miss, n_no_pkt_in, n_bytes, n_dropped_frags;
uint64_t n_lookup;
strcpy(ots->name, "classifier");
dpif_get_dp_stats(ofproto->backer->dpif, &s);
rule_get_stats(&ofproto->miss_rule->up, &n_miss, &n_bytes);
rule_get_stats(&ofproto->no_packet_in_rule->up, &n_no_pkt_in, &n_bytes);
+ rule_get_stats(&ofproto->drop_frags_rule->up, &n_dropped_frags, &n_bytes);
- n_lookup = s.n_hit + s.n_missed;
+ n_lookup = s.n_hit + s.n_missed - n_dropped_frags;
ots->lookup_count = htonll(n_lookup);
ots->matched_count = htonll(n_lookup - n_miss - n_no_pkt_in);
}
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
const struct netdev *netdev = port->up.netdev;
+ char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
struct dpif_port dpif_port;
int error;
}
error = dpif_port_query_by_name(ofproto->backer->dpif,
- netdev_vport_get_dpif_port(netdev),
+ netdev_vport_get_dpif_port(netdev, namebuf,
+ sizeof namebuf),
&dpif_port);
if (error) {
return error;
{
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
- const char *dp_port_name = netdev_vport_get_dpif_port(port->up.netdev);
const char *devname = netdev_get_name(port->up.netdev);
+ char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+ const char *dp_port_name;
+ dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf,
+ sizeof namebuf);
if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
/* The underlying device is still there, so delete it. This
* happens when the ofproto is being destroyed, since the caller
if (port->bundle && port->bundle->bond) {
bond_slave_set_netdev(port->bundle->bond, port, port->up.netdev);
}
+
+ if (port->cfm) {
+ cfm_set_netdev(port->cfm, port->up.netdev);
+ }
}
static void
port_add(struct ofproto *ofproto_, struct netdev *netdev)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- const char *dp_port_name = netdev_vport_get_dpif_port(netdev);
const char *devname = netdev_get_name(netdev);
+ char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+ const char *dp_port_name;
if (netdev_vport_is_patch(netdev)) {
sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
return 0;
}
+ dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
uint32_t port_no = UINT32_MAX;
int error;
* the benefits, so when the datapath holds a large number of flows we impose
* some heuristics to decide which flows are likely to be worth tracking. */
static bool
-flow_miss_should_make_facet(struct ofproto_dpif *ofproto,
- struct flow_miss *miss, uint32_t hash)
+flow_miss_should_make_facet(struct flow_miss *miss, uint32_t hash)
{
- if (!ofproto->governor) {
+ struct dpif_backer *backer = miss->ofproto->backer;
+
+ if (!backer->governor) {
size_t n_subfacets;
- n_subfacets = hmap_count(&ofproto->subfacets);
- if (n_subfacets * 2 <= ofproto->up.flow_eviction_threshold) {
+ n_subfacets = hmap_count(&backer->subfacets);
+ if (n_subfacets * 2 <= flow_eviction_threshold) {
return true;
}
- ofproto->governor = governor_create(ofproto->up.name);
+ backer->governor = governor_create();
}
- return governor_should_install_flow(ofproto->governor, hash,
+ return governor_should_install_flow(backer->governor, hash,
list_size(&miss->packets));
}
* flow keys with fitness ODP_FIT_TO_LITTLE. This breaks a fundamental
* assumption used throughout the facet and subfacet handling code.
* Since we have to handle these misses in userspace anyway, we simply
- * skip facet creation, avoiding the problem alltogether. */
+ * skip facet creation, avoiding the problem altogether. */
if (miss->key_fitness == ODP_FIT_TOO_LITTLE
- || !flow_miss_should_make_facet(ofproto, miss, hash)) {
+ || !flow_miss_should_make_facet(miss, hash)) {
handle_flow_miss_without_facet(miss, ops, n_ops);
return;
}
\f
/* Flow expiration. */
-static int subfacet_max_idle(const struct ofproto_dpif *);
+static int subfacet_max_idle(const struct dpif_backer *);
static void update_stats(struct dpif_backer *);
static void rule_expire(struct rule_dpif *);
-static void expire_subfacets(struct ofproto_dpif *, int dp_max_idle);
+static void expire_subfacets(struct dpif_backer *, int dp_max_idle);
/* This function is called periodically by run(). Its job is to collect
* updates for the flows that have been installed into the datapath, most
expire(struct dpif_backer *backer)
{
struct ofproto_dpif *ofproto;
- int max_idle = INT32_MAX;
+ size_t n_subfacets;
+ int max_idle;
/* Periodically clear out the drop keys in an effort to keep them
* relatively few. */
/* Update stats for each flow in the backer. */
update_stats(backer);
+ n_subfacets = hmap_count(&backer->subfacets);
+ if (n_subfacets) {
+ struct subfacet *subfacet;
+ long long int total, now;
+
+ total = 0;
+ now = time_msec();
+ HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
+ total += now - subfacet->created;
+ }
+ backer->avg_subfacet_life += total / n_subfacets;
+ }
+ backer->avg_subfacet_life /= 2;
+
+ backer->avg_n_subfacet += n_subfacets;
+ backer->avg_n_subfacet /= 2;
+
+ backer->max_n_subfacet = MAX(backer->max_n_subfacet, n_subfacets);
+
+ max_idle = subfacet_max_idle(backer);
+ expire_subfacets(backer, max_idle);
+
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
struct rule *rule, *next_rule;
- int dp_max_idle;
if (ofproto->backer != backer) {
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);
-
- max_idle = MIN(max_idle, dp_max_idle);
-
/* Expire OpenFlow flows whose idle_timeout or hard_timeout
* has passed. */
LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
/* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
* about, or a flow that shouldn't be installed but was anyway. Delete it. */
static void
-delete_unexpected_flow(struct ofproto_dpif *ofproto,
+delete_unexpected_flow(struct dpif_backer *backer,
const struct nlattr *key, size_t key_len)
{
if (!VLOG_DROP_WARN(&rl)) {
ds_init(&s);
odp_flow_key_format(key, key_len, &s);
- VLOG_WARN("unexpected flow on %s: %s", ofproto->up.name, ds_cstr(&s));
+ VLOG_WARN("unexpected flow: %s", ds_cstr(&s));
ds_destroy(&s);
}
COVERAGE_INC(facet_unexpected);
- dpif_flow_del(ofproto->backer->dpif, key, key_len, NULL);
+ dpif_flow_del(backer->dpif, key, key_len, NULL);
}
/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
const struct dpif_flow_stats *stats;
struct dpif_flow_dump dump;
const struct nlattr *key;
- struct ofproto_dpif *ofproto;
size_t key_len;
dpif_flow_dump_start(&dump, backer->dpif);
while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
- struct flow flow;
struct subfacet *subfacet;
uint32_t key_hash;
- if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
- NULL, NULL)) {
- continue;
- }
-
- ofproto->total_subfacet_count += hmap_count(&ofproto->subfacets);
- ofproto->n_update_stats++;
-
key_hash = odp_flow_key_hash(key, key_len);
- subfacet = subfacet_find(ofproto, key, key_len, key_hash);
+ subfacet = subfacet_find(backer, key, key_len, key_hash);
switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
case SF_FAST_PATH:
update_subfacet_stats(subfacet, stats);
case SF_NOT_INSTALLED:
default:
- delete_unexpected_flow(ofproto, key, key_len);
+ delete_unexpected_flow(backer, key, key_len);
break;
}
run_fast_rl();
}
dpif_flow_dump_done(&dump);
- HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- update_moving_averages(ofproto);
- }
-
+ update_moving_averages(backer);
}
/* Calculates and returns the number of milliseconds of idle time after which
* its statistics into its facet, and when a facet's last subfacet expires, we
* fold its statistic into its rule. */
static int
-subfacet_max_idle(const struct ofproto_dpif *ofproto)
+subfacet_max_idle(const struct dpif_backer *backer)
{
/*
* Idle time histogram.
* that is installed in the kernel gets dropped in the appropriate bucket.
* After the histogram has been built, we compute the cutoff so that only
* the most-recently-used 1% of subfacets (but at least
- * ofproto->up.flow_eviction_threshold flows) are kept cached. At least
+ * flow_eviction_threshold flows) are kept cached. At least
* the most-recently-used bucket of subfacets is kept, so actually an
* arbitrary number of subfacets can be kept in any given expiration run
* (though the next run will delete most of those unless they receive
long long int now;
int i;
- total = hmap_count(&ofproto->subfacets);
- if (total <= ofproto->up.flow_eviction_threshold) {
+ total = hmap_count(&backer->subfacets);
+ if (total <= flow_eviction_threshold) {
return N_BUCKETS * BUCKET_WIDTH;
}
/* Build histogram. */
now = time_msec();
- HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+ HMAP_FOR_EACH (subfacet, hmap_node, &backer->subfacets) {
long long int idle = now - subfacet->used;
int bucket = (idle <= 0 ? 0
: idle >= BUCKET_WIDTH * N_BUCKETS ? N_BUCKETS - 1
do {
subtotal += buckets[bucket++];
} while (bucket < N_BUCKETS &&
- subtotal < MAX(ofproto->up.flow_eviction_threshold, total / 100));
+ subtotal < MAX(flow_eviction_threshold, total / 100));
if (VLOG_IS_DBG_ENABLED()) {
struct ds s;
ds_put_format(&s, " %d:%d", i * BUCKET_WIDTH, buckets[i]);
}
}
- VLOG_INFO("%s: %s (msec:count)", ofproto->up.name, ds_cstr(&s));
+ VLOG_INFO("%s (msec:count)", ds_cstr(&s));
ds_destroy(&s);
}
}
static void
-expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
+expire_subfacets(struct dpif_backer *backer, int dp_max_idle)
{
/* Cutoff time for most flows. */
long long int normal_cutoff = time_msec() - dp_max_idle;
n_batch = 0;
HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
- &ofproto->subfacets) {
+ &backer->subfacets) {
long long int cutoff;
cutoff = (subfacet->facet->xout.slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP
if (subfacet->path != SF_NOT_INSTALLED) {
batch[n_batch++] = subfacet;
if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
- subfacet_destroy_batch(ofproto, batch, n_batch);
+ subfacet_destroy_batch(backer, batch, n_batch);
n_batch = 0;
}
} else {
}
if (n_batch > 0) {
- subfacet_destroy_batch(ofproto, batch, n_batch);
+ subfacet_destroy_batch(backer, batch, n_batch);
}
}
ds_put_format(&s, ") (should have been table=%"PRIu8",",
rule->up.table_id);
cls_rule_format(&rule->up.cr, &s);
- ds_put_cstr(&s, ")\n");
+ ds_put_char(&s, ')');
+ VLOG_WARN("%s", ds_cstr(&s));
ds_destroy(&s);
}
return false;
ds_put_cstr(&s, ") (correct actions: ");
format_odp_actions(&s, xout.odp_actions.data,
xout.odp_actions.size);
- ds_put_cstr(&s, ")");
+ ds_put_char(&s, ')');
}
if (facet->xout.slow != xout.slow) {
ds_put_format(&s, " slow path incorrect. should be %d", xout.slow);
}
+ VLOG_WARN("%s", ds_cstr(&s));
ds_destroy(&s);
}
xlate_out_uninit(&xout);
/* Subfacets. */
static struct subfacet *
-subfacet_find(struct ofproto_dpif *ofproto,
- const struct nlattr *key, size_t key_len, uint32_t key_hash)
+subfacet_find(struct dpif_backer *backer, const struct nlattr *key,
+ size_t key_len, uint32_t key_hash)
{
struct subfacet *subfacet;
HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
- &ofproto->subfacets) {
+ &backer->subfacets) {
if (subfacet->key_len == key_len
&& !memcmp(key, subfacet->key, key_len)) {
return subfacet;
subfacet_create(struct facet *facet, struct flow_miss *miss,
long long int now)
{
- struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ struct dpif_backer *backer = miss->ofproto->backer;
enum odp_key_fitness key_fitness = miss->key_fitness;
const struct nlattr *key = miss->key;
size_t key_len = miss->key_len;
if (list_is_empty(&facet->subfacets)) {
subfacet = &facet->one_subfacet;
} else {
- subfacet = subfacet_find(ofproto, key, key_len, key_hash);
+ subfacet = subfacet_find(backer, key, key_len, key_hash);
if (subfacet) {
if (subfacet->facet == facet) {
return subfacet;
subfacet = xmalloc(sizeof *subfacet);
}
- hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
+ hmap_insert(&backer->subfacets, &subfacet->hmap_node, key_hash);
list_push_back(&facet->subfacets, &subfacet->list_node);
subfacet->facet = facet;
subfacet->key_fitness = key_fitness;
subfacet->dp_packet_count = 0;
subfacet->dp_byte_count = 0;
subfacet->path = SF_NOT_INSTALLED;
+ subfacet->backer = backer;
- ofproto->subfacet_add_count++;
+ backer->subfacet_add_count++;
return subfacet;
}
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);
+ ofproto->backer->subfacet_del_count++;
subfacet_uninstall(subfacet);
- hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
+ hmap_remove(&subfacet->backer->subfacets, &subfacet->hmap_node);
list_remove(&subfacet->list_node);
free(subfacet->key);
if (subfacet != &facet->one_subfacet) {
}
static void
-subfacet_destroy_batch(struct ofproto_dpif *ofproto,
+subfacet_destroy_batch(struct dpif_backer *backer,
struct subfacet **subfacets, int n)
{
struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
opsp[i] = &ops[i];
}
- dpif_operate(ofproto->backer->dpif, opsp, n);
+ dpif_operate(backer->dpif, opsp, n);
for (i = 0; i < n; i++) {
subfacet_reset_dp_stats(subfacets[i], &stats[i]);
subfacets[i]->path = SF_NOT_INSTALLED;
&actions, &actions_len);
}
- ret = dpif_flow_put(ofproto->backer->dpif, flags, subfacet->key,
+ ret = dpif_flow_put(subfacet->backer->dpif, flags, subfacet->key,
subfacet->key_len, actions, actions_len, stats);
if (stats) {
{
struct cls_rule *cls_rule;
struct classifier *cls;
+ bool frag;
if (table_id >= N_TABLES) {
return NULL;
}
cls = &ofproto->up.tables[table_id].cls;
- if (flow->nw_frag & FLOW_NW_FRAG_ANY
- && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
- /* For OFPC_NORMAL frag_handling, we must pretend that transport ports
- * are unavailable. */
+ frag = (flow->nw_frag & FLOW_NW_FRAG_ANY) != 0;
+ if (frag && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
+ /* We must pretend that transport ports are unavailable. */
struct flow ofpc_normal_flow = *flow;
ofpc_normal_flow.tp_src = htons(0);
ofpc_normal_flow.tp_dst = htons(0);
cls_rule = classifier_lookup(cls, &ofpc_normal_flow);
+ } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) {
+ cls_rule = &ofproto->drop_frags_rule->up.cr;
} else {
cls_rule = classifier_lookup(cls, flow);
}
&ctx->xout->odp_actions);
ctx->xin->flow.tunnel = flow_tnl; /* Restore tunnel metadata */
} else {
+ uint16_t vlandev_port;
odp_port = ofport->odp_port;
- out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
- ctx->xin->flow.vlan_tci);
- if (out_port != odp_port) {
+ vlandev_port = vsp_realdev_to_vlandev(ctx->ofproto, ofp_port,
+ ctx->xin->flow.vlan_tci);
+ if (vlandev_port == ofp_port) {
+ out_port = odp_port;
+ } else {
+ out_port = ofp_port_to_odp_port(ctx->ofproto, vlandev_port);
ctx->xin->flow.vlan_tci = htons(0);
}
ctx->xin->flow.skb_mark &= ~IPSEC_MARK;
{
struct ofputil_packet_in pin;
struct ofpbuf *packet;
+ struct flow key;
ovs_assert(!ctx->xout->slow || ctx->xout->slow == SLOW_CONTROLLER);
ctx->xout->slow = SLOW_CONTROLLER;
packet = ofpbuf_clone(ctx->xin->packet);
- if (packet->l2 && packet->l3) {
- struct eth_header *eh;
- uint16_t mpls_depth;
-
- eth_pop_vlan(packet);
- eh = packet->l2;
+ key.skb_priority = 0;
+ key.skb_mark = 0;
+ memset(&key.tunnel, 0, sizeof key.tunnel);
- memcpy(eh->eth_src, ctx->xin->flow.dl_src, sizeof eh->eth_src);
- memcpy(eh->eth_dst, ctx->xin->flow.dl_dst, sizeof eh->eth_dst);
-
- if (ctx->xin->flow.vlan_tci & htons(VLAN_CFI)) {
- eth_push_vlan(packet, ctx->xin->flow.vlan_tci);
- }
-
- mpls_depth = eth_mpls_depth(packet);
-
- if (mpls_depth < ctx->xin->flow.mpls_depth) {
- push_mpls(packet, ctx->xin->flow.dl_type, ctx->xin->flow.mpls_lse);
- } else if (mpls_depth > ctx->xin->flow.mpls_depth) {
- pop_mpls(packet, ctx->xin->flow.dl_type);
- } else if (mpls_depth) {
- set_mpls_lse(packet, ctx->xin->flow.mpls_lse);
- }
-
- if (packet->l4) {
- if (ctx->xin->flow.dl_type == htons(ETH_TYPE_IP)) {
- packet_set_ipv4(packet, ctx->xin->flow.nw_src,
- ctx->xin->flow.nw_dst, ctx->xin->flow.nw_tos,
- ctx->xin->flow.nw_ttl);
- }
+ commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+ &ctx->xout->odp_actions);
- if (packet->l7) {
- if (ctx->xin->flow.nw_proto == IPPROTO_TCP) {
- packet_set_tcp_port(packet, ctx->xin->flow.tp_src,
- ctx->xin->flow.tp_dst);
- } else if (ctx->xin->flow.nw_proto == IPPROTO_UDP) {
- packet_set_udp_port(packet, ctx->xin->flow.tp_src,
- ctx->xin->flow.tp_dst);
- }
- }
- }
- }
+ odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
+ ctx->xout->odp_actions.size, NULL, NULL);
pin.packet = packet->data;
pin.packet_len = packet->size;
} 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) {
}
static void
-show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
+show_dp_rates(struct ds *ds, const char *heading,
+ const struct avg_subfacet_rates *rates)
{
- 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. */
+ ds_put_format(ds, "%s add rate: %5.3f/min, del rate: %5.3f/min\n",
+ heading, rates->add_rate, rates->del_rate);
+}
- minutes = (time_msec() - ofproto->created) / min_ms;
+static void
+dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
+{
+ size_t n_hit, n_missed, i;
+ const struct shash_node **ofprotos;
+ struct ofproto_dpif *ofproto;
+ struct shash ofproto_shash;
+ long long int minutes;
- 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: 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));
+ n_hit = n_missed = 0;
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ if (ofproto->backer == backer) {
+ n_missed += ofproto->n_missed;
+ n_hit += ofproto->n_hit;
+ }
+ }
+
+ 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,"
+ " life span: %lldms\n", hmap_count(&backer->subfacets),
+ backer->avg_n_subfacet, backer->max_n_subfacet,
+ backer->avg_subfacet_life);
+
+ minutes = (time_msec() - backer->created) / (1000 * 60);
if (minutes >= 60) {
- show_dp_rates(ds, "\t\thourly avg:", &ofproto->hourly);
+ show_dp_rates(ds, "\thourly avg:", &backer->hourly);
}
if (minutes >= 60 * 24) {
- show_dp_rates(ds, "\t\tdaily avg:", &ofproto->daily);
+ show_dp_rates(ds, "\tdaily avg:", &backer->daily);
}
- show_dp_rates(ds, "\t\toverall avg:", &lifetime);
+ show_dp_rates(ds, "\toverall avg:", &backer->lifetime);
- ports = shash_sort(&ofproto->up.port_by_name);
- for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) {
- const struct shash_node *node = ports[i];
- struct ofport *ofport = node->data;
- const char *name = netdev_get_name(ofport->netdev);
- const char *type = netdev_get_type(ofport->netdev);
- uint32_t odp_port;
-
- ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+ shash_init(&ofproto_shash);
+ ofprotos = get_ofprotos(&ofproto_shash);
+ for (i = 0; i < shash_count(&ofproto_shash); i++) {
+ struct ofproto_dpif *ofproto = ofprotos[i]->data;
+ const struct shash_node **ports;
+ size_t j;
- odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
- if (odp_port != OVSP_NONE) {
- ds_put_format(ds, "%"PRIu32":", odp_port);
- } else {
- ds_put_cstr(ds, "none:");
+ if (ofproto->backer != backer) {
+ continue;
}
- if (strcmp(type, "system")) {
- struct netdev *netdev;
- int error;
+ ds_put_format(ds, "\t%s: hit:%"PRIu64" missed:%"PRIu64"\n",
+ ofproto->up.name, ofproto->n_hit, ofproto->n_missed);
- ds_put_format(ds, " (%s", type);
+ ports = shash_sort(&ofproto->up.port_by_name);
+ for (j = 0; j < shash_count(&ofproto->up.port_by_name); j++) {
+ const struct shash_node *node = ports[j];
+ struct ofport *ofport = node->data;
+ struct smap config;
+ uint32_t odp_port;
- error = netdev_open(name, type, &netdev);
- if (!error) {
- struct smap config;
+ ds_put_format(ds, "\t\t%s %u/", netdev_get_name(ofport->netdev),
+ ofport->ofp_port);
- smap_init(&config);
- error = netdev_get_config(netdev, &config);
- if (!error) {
- const struct smap_node **nodes;
- size_t i;
+ odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+ if (odp_port != OVSP_NONE) {
+ ds_put_format(ds, "%"PRIu32":", odp_port);
+ } else {
+ ds_put_cstr(ds, "none:");
+ }
- nodes = smap_sort(&config);
- for (i = 0; i < smap_count(&config); i++) {
- const struct smap_node *node = nodes[i];
- ds_put_format(ds, "%c %s=%s", i ? ',' : ':',
- node->key, node->value);
- }
- free(nodes);
- }
- smap_destroy(&config);
+ ds_put_format(ds, " (%s", netdev_get_type(ofport->netdev));
- netdev_close(netdev);
+ smap_init(&config);
+ if (!netdev_get_config(ofport->netdev, &config)) {
+ const struct smap_node **nodes;
+ size_t i;
+
+ nodes = smap_sort(&config);
+ for (i = 0; i < smap_count(&config); i++) {
+ const struct smap_node *node = nodes[i];
+ ds_put_format(ds, "%c %s=%s", i ? ',' : ':',
+ node->key, node->value);
+ }
+ free(nodes);
}
+ smap_destroy(&config);
+
ds_put_char(ds, ')');
+ ds_put_char(ds, '\n');
}
- ds_put_char(ds, '\n');
+ free(ports);
}
- free(ports);
+ shash_destroy(&ofproto_shash);
+ free(ofprotos);
}
static void
-ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc,
- const char *argv[], void *aux OVS_UNUSED)
+ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
{
struct ds ds = DS_EMPTY_INITIALIZER;
- const struct ofproto_dpif *ofproto;
-
- if (argc > 1) {
- int i;
- for (i = 1; i < argc; i++) {
- ofproto = ofproto_dpif_lookup(argv[i]);
- if (!ofproto) {
- ds_put_format(&ds, "Unknown bridge %s (use dpif/dump-dps "
- "for help)", argv[i]);
- unixctl_command_reply_error(conn, ds_cstr(&ds));
- return;
- }
- show_dp_format(ofproto, &ds);
- }
- } else {
- struct shash ofproto_shash;
- const struct shash_node **sorted_ofprotos;
- int i;
-
- shash_init(&ofproto_shash);
- sorted_ofprotos = get_ofprotos(&ofproto_shash);
- for (i = 0; i < shash_count(&ofproto_shash); i++) {
- const struct shash_node *node = sorted_ofprotos[i];
- show_dp_format(node->data, &ds);
- }
+ const struct shash_node **backers;
+ int i;
- shash_destroy(&ofproto_shash);
- free(sorted_ofprotos);
+ backers = shash_sort(&all_dpif_backers);
+ for (i = 0; i < shash_count(&all_dpif_backers); i++) {
+ dpif_show_backer(backers[i]->data, &ds);
}
+ free(backers);
unixctl_command_reply(conn, ds_cstr(&ds));
ds_destroy(&ds);
update_stats(ofproto->backer);
- HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+ HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->backer->subfacets) {
struct facet *facet = subfacet->facet;
+ if (ofproto_dpif_cast(facet->rule->up.ofproto) != ofproto) {
+ continue;
+ }
+
odp_flow_key_format(subfacet->key, subfacet->key_len, &ds);
ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
ofproto_dpif_self_check, NULL);
unixctl_command_register("dpif/dump-dps", "", 0, 0,
ofproto_unixctl_dpif_dump_dps, NULL);
- unixctl_command_register("dpif/show", "[bridge]", 0, INT_MAX,
- ofproto_unixctl_dpif_show, NULL);
+ unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show,
+ NULL);
unixctl_command_register("dpif/dump-flows", "bridge", 1, 1,
ofproto_unixctl_dpif_dump_flows, NULL);
unixctl_command_register("dpif/del-flows", "bridge", 1, 1,
return hash_2words(realdev_ofp_port, vid);
}
-/* Returns the ODP port number of the Linux VLAN device that corresponds to
- * 'vlan_tci' on the network device with port number 'realdev_odp_port' in
- * 'ofproto'. For example, given 'realdev_odp_port' of eth0 and 'vlan_tci' 9,
- * it would return the port number of eth0.9.
+/* Returns the OFP port number of the Linux VLAN device that corresponds to
+ * 'vlan_tci' on the network device with port number 'realdev_ofp_port' in
+ * 'struct ofport_dpif'. For example, given 'realdev_ofp_port' of eth0 and
+ * 'vlan_tci' 9, it would return the port number of eth0.9.
*
- * Unless VLAN splinters are enabled for port 'realdev_odp_port', this
- * function just returns its 'realdev_odp_port' argument. */
-static uint32_t
+ * Unless VLAN splinters are enabled for port 'realdev_ofp_port', this
+ * function just returns its 'realdev_ofp_port' argument. */
+static uint16_t
vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto,
- uint32_t realdev_odp_port, ovs_be16 vlan_tci)
+ uint16_t realdev_ofp_port, ovs_be16 vlan_tci)
{
if (!hmap_is_empty(&ofproto->realdev_vid_map)) {
- uint16_t realdev_ofp_port;
int vid = vlan_tci_to_vid(vlan_tci);
const struct vlan_splinter *vsp;
- realdev_ofp_port = odp_port_to_ofp_port(ofproto, realdev_odp_port);
HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node,
hash_realdev_vid(realdev_ofp_port, vid),
&ofproto->realdev_vid_map) {
if (vsp->realdev_ofp_port == realdev_ofp_port
&& vsp->vid == vid) {
- return ofp_port_to_odp_port(ofproto, vsp->vlandev_ofp_port);
+ return vsp->vlandev_ofp_port;
}
}
}
- return realdev_odp_port;
+ return realdev_ofp_port;
}
static struct vlan_splinter *
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
}
static void
-update_moving_averages(struct ofproto_dpif *ofproto)
+update_moving_averages(struct dpif_backer *backer)
{
const int min_ms = 60 * 1000; /* milliseconds in one minute. */
+ long long int minutes = (time_msec() - backer->created) / min_ms;
+
+ if (minutes > 0) {
+ backer->lifetime.add_rate = (double) backer->total_subfacet_add_count
+ / minutes;
+ backer->lifetime.del_rate = (double) backer->total_subfacet_del_count
+ / minutes;
+ } else {
+ backer->lifetime.add_rate = 0.0;
+ backer->lifetime.del_rate = 0.0;
+ }
/* 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);
+ if (time_msec() - backer->last_minute >= min_ms) {
+ exp_mavg(&backer->hourly.add_rate, 60, backer->subfacet_add_count);
+ exp_mavg(&backer->hourly.del_rate, 60, backer->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);
+ if ((backer->last_minute - backer->created) / min_ms % 60 == 59) {
+ exp_mavg(&backer->daily.add_rate, 24, backer->hourly.add_rate);
+ exp_mavg(&backer->daily.del_rate, 24, backer->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;
+ backer->total_subfacet_add_count += backer->subfacet_add_count;
+ backer->total_subfacet_del_count += backer->subfacet_del_count;
+ backer->subfacet_add_count = 0;
+ backer->subfacet_del_count = 0;
+ backer->last_minute += min_ms;
}
}