#include "dpif.h"
#include "dynamic-string.h"
#include "fail-open.h"
+#include "guarded-list.h"
#include "hmapx.h"
#include "lacp.h"
#include "learn.h"
VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
COVERAGE_DEFINE(ofproto_dpif_expired);
-COVERAGE_DEFINE(facet_changed_rule);
COVERAGE_DEFINE(facet_revalidate);
COVERAGE_DEFINE(facet_unexpected);
-COVERAGE_DEFINE(facet_suppress);
+COVERAGE_DEFINE(facet_create);
+COVERAGE_DEFINE(facet_remove);
+COVERAGE_DEFINE(subfacet_create);
+COVERAGE_DEFINE(subfacet_destroy);
COVERAGE_DEFINE(subfacet_install_fail);
COVERAGE_DEFINE(packet_in_overflow);
-COVERAGE_DEFINE(flow_mod_overflow);
/* Number of implemented OpenFlow tables. */
enum { N_TABLES = 255 };
struct flow_miss;
struct facet;
+struct rule_dpif {
+ struct rule 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. */
+};
+
static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
+static struct rule_dpif *rule_dpif_cast(const struct rule *);
struct ofbundle {
struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
struct facet *facet; /* Owning facet. */
struct dpif_backer *backer; /* Owning backer. */
- enum odp_key_fitness key_fitness;
struct nlattr *key;
int key_len;
* Flow expiration works in terms of subfacets, so a facet must have at
* least one subfacet or it will never expire, leaking memory. */
struct facet {
- /* Owners. */
- struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */
+ /* Owner. */
struct ofproto_dpif *ofproto;
/* Owned data. */
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;
-
/* Number of upcall handling threads. */
unsigned int n_handler_threads;
};
static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
static void drop_key_clear(struct dpif_backer *);
-static void update_moving_averages(struct dpif_backer *backer);
struct ofproto_dpif {
struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
struct classifier facets; /* Contains 'struct facet's. */
long long int consistency_rl;
- /* Support for debugging async flow mods. */
- struct list completions;
-
struct netdev_stats stats; /* To account packets generated and consumed in
* userspace. */
uint64_t n_missed;
/* Work queues. */
- struct ovs_mutex flow_mod_mutex;
- struct list flow_mods OVS_GUARDED;
- size_t n_flow_mods OVS_GUARDED;
-
- struct ovs_mutex pin_mutex;
- struct list pins OVS_GUARDED;
- size_t n_pins OVS_GUARDED;
+ struct guarded_list pins; /* Contains "struct ofputil_packet_in"s. */
};
-/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
- * for debugging the asynchronous flow_mod implementation.) */
-static bool clogged;
-
/* By default, flows in the datapath are wildcarded (megaflows). They
* may be disabled with the "ovs-appctl dpif/disable-megaflows" command. */
static bool enable_megaflows = true;
/* Initial mappings of port to bridge mappings. */
static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
-/* Executes and takes ownership of 'fm'. */
+/* Executes 'fm'. The caller retains ownership of 'fm' and everything in
+ * it. */
void
ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto,
struct ofputil_flow_mod *fm)
{
- ovs_mutex_lock(&ofproto->flow_mod_mutex);
- if (ofproto->n_flow_mods > 1024) {
- ovs_mutex_unlock(&ofproto->flow_mod_mutex);
- COVERAGE_INC(flow_mod_overflow);
- free(fm->ofpacts);
- free(fm);
- return;
- }
-
- list_push_back(&ofproto->flow_mods, &fm->list_node);
- ofproto->n_flow_mods++;
- ovs_mutex_unlock(&ofproto->flow_mod_mutex);
+ ofproto_flow_mod(&ofproto->up, fm);
}
/* Appends 'pin' to the queue of "packet ins" to be sent to the controller.
ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto,
struct ofputil_packet_in *pin)
{
- ovs_mutex_lock(&ofproto->pin_mutex);
- if (ofproto->n_pins > 1024) {
- ovs_mutex_unlock(&ofproto->pin_mutex);
+ if (!guarded_list_push_back(&ofproto->pins, &pin->list_node, 1024)) {
COVERAGE_INC(packet_in_overflow);
free(CONST_CAST(void *, pin->packet));
free(pin);
- return;
}
-
- list_push_back(&ofproto->pins, &pin->list_node);
- ofproto->n_pins++;
- ovs_mutex_unlock(&ofproto->pin_mutex);
}
\f
/* Factory functions. */
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
int stp_port = ofport->stp_port
? stp_port_no(ofport->stp_port)
- : 0;
+ : -1;
xlate_ofport_set(ofproto, ofport->bundle, ofport,
ofport->up.ofp_port, ofport->odp_port,
ofport->up.netdev, ofport->cfm,
static int
dpif_backer_run_fast(struct dpif_backer *backer)
{
- udpif_run(backer->udpif);
handle_upcalls(backer);
return 0;
static void
close_dpif_backer(struct dpif_backer *backer)
{
- struct shash_node *node;
-
ovs_assert(backer->refcount > 0);
if (--backer->refcount) {
drop_key_clear(backer);
hmap_destroy(&backer->drop_keys);
+ udpif_destroy(backer->udpif);
+
simap_destroy(&backer->tnl_backers);
ovs_rwlock_destroy(&backer->odp_to_ofport_lock);
hmap_destroy(&backer->odp_to_ofport_map);
- node = shash_find(&all_dpif_backers, backer->type);
+ shash_find_and_delete(&all_dpif_backers, backer->type);
free(backer->type);
- shash_delete(&all_dpif_backers, node);
- udpif_destroy(backer->udpif);
dpif_close(backer->dpif);
ovs_assert(hmap_is_empty(&backer->subfacets));
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;
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct shash_node *node, *next;
- odp_port_t max_ports;
+ uint32_t max_ports;
int error;
error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
}
max_ports = dpif_get_max_ports(ofproto->backer->dpif);
- ofproto_init_max_ports(ofproto_, u16_to_ofp(MIN(odp_to_u32(max_ports),
- ofp_to_u16(OFPP_MAX))));
+ ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX)));
ofproto->netflow = NULL;
ofproto->sflow = NULL;
classifier_init(&ofproto->facets);
ofproto->consistency_rl = LLONG_MIN;
- list_init(&ofproto->completions);
-
- ovs_mutex_init(&ofproto->flow_mod_mutex);
- ovs_mutex_lock(&ofproto->flow_mod_mutex);
- list_init(&ofproto->flow_mods);
- ofproto->n_flow_mods = 0;
- ovs_mutex_unlock(&ofproto->flow_mod_mutex);
-
- ovs_mutex_init(&ofproto->pin_mutex);
- ovs_mutex_lock(&ofproto->pin_mutex);
- list_init(&ofproto->pins);
- ofproto->n_pins = 0;
- ovs_mutex_unlock(&ofproto->pin_mutex);
+ guarded_list_init(&ofproto->pins);
ofproto_dpif_unixctl_init();
if (rule_dpif_lookup_in_table(ofproto, &fm.match.flow, NULL, TBL_INTERNAL,
rulep)) {
- ovs_rwlock_unlock(&(*rulep)->up.evict);
+ rule_dpif_unref(*rulep);
} else {
NOT_REACHED();
}
return error;
}
-static void
-complete_operations(struct ofproto_dpif *ofproto)
-{
- struct dpif_completion *c, *next;
-
- LIST_FOR_EACH_SAFE (c, next, list_node, &ofproto->completions) {
- ofoperation_complete(c->op, 0);
- list_remove(&c->list_node);
- free(c);
- }
-}
-
static void
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 ofputil_flow_mod *fm, *next_fm;
+ struct facet *facet, *next_facet;
+ struct cls_cursor cursor;
struct oftable *table;
+ struct list pins;
+
+ ovs_rwlock_rdlock(&ofproto->facets.rwlock);
+ cls_cursor_init(&cursor, &ofproto->facets, NULL);
+ ovs_rwlock_unlock(&ofproto->facets.rwlock);
+ CLS_CURSOR_FOR_EACH_SAFE (facet, next_facet, cr, &cursor) {
+ facet_remove(facet);
+ }
ofproto->backer->need_revalidate = REV_RECONFIGURE;
ovs_rwlock_wrlock(&xlate_rwlock);
xlate_remove_ofproto(ofproto);
ovs_rwlock_unlock(&xlate_rwlock);
+ /* Discard any flow_miss_batches queued up for 'ofproto', avoiding a
+ * use-after-free error. */
+ udpif_revalidate(ofproto->backer->udpif);
+
hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
- complete_operations(ofproto);
OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
struct cls_cursor cursor;
- ovs_rwlock_wrlock(&table->cls.rwlock);
+ ovs_rwlock_rdlock(&table->cls.rwlock);
cls_cursor_init(&cursor, &table->cls, NULL);
+ ovs_rwlock_unlock(&table->cls.rwlock);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
- ofproto_rule_destroy(&ofproto->up, &table->cls, &rule->up);
+ ofproto_rule_delete(&ofproto->up, &rule->up);
}
- ovs_rwlock_unlock(&table->cls.rwlock);
}
- ovs_mutex_lock(&ofproto->flow_mod_mutex);
- LIST_FOR_EACH_SAFE (fm, next_fm, list_node, &ofproto->flow_mods) {
- list_remove(&fm->list_node);
- ofproto->n_flow_mods--;
- free(fm->ofpacts);
- free(fm);
- }
- ovs_mutex_unlock(&ofproto->flow_mod_mutex);
- ovs_mutex_destroy(&ofproto->flow_mod_mutex);
-
- ovs_mutex_lock(&ofproto->pin_mutex);
- LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &ofproto->pins) {
+ guarded_list_pop_all(&ofproto->pins, &pins);
+ LIST_FOR_EACH_SAFE (pin, next_pin, list_node, &pins) {
list_remove(&pin->list_node);
- ofproto->n_pins--;
free(CONST_CAST(void *, pin->packet));
free(pin);
}
- ovs_mutex_unlock(&ofproto->pin_mutex);
- ovs_mutex_destroy(&ofproto->pin_mutex);
+ guarded_list_destroy(&ofproto->pins);
mbridge_unref(ofproto->mbridge);
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct ofputil_packet_in *pin, *next_pin;
- struct ofputil_flow_mod *fm, *next_fm;
- struct list flow_mods, pins;
struct ofport_dpif *ofport;
+ struct list pins;
/* Do not perform any periodic activity required by 'ofproto' while
* waiting for flow restore to complete. */
return 0;
}
- ovs_mutex_lock(&ofproto->flow_mod_mutex);
- if (ofproto->n_flow_mods) {
- flow_mods = ofproto->flow_mods;
- list_moved(&flow_mods);
- list_init(&ofproto->flow_mods);
- ofproto->n_flow_mods = 0;
- } else {
- list_init(&flow_mods);
- }
- ovs_mutex_unlock(&ofproto->flow_mod_mutex);
-
- LIST_FOR_EACH_SAFE (fm, next_fm, list_node, &flow_mods) {
- int error = ofproto_flow_mod(&ofproto->up, fm);
- if (error && !VLOG_DROP_WARN(&rl)) {
- VLOG_WARN("learning action failed to modify flow table (%s)",
- ofperr_get_name(error));
- }
-
- list_remove(&fm->list_node);
- free(fm->ofpacts);
- free(fm);
- }
-
- ovs_mutex_lock(&ofproto->pin_mutex);
- if (ofproto->n_pins) {
- pins = ofproto->pins;
- list_moved(&pins);
- list_init(&ofproto->pins);
- ofproto->n_pins = 0;
- } else {
- list_init(&pins);
- }
- ovs_mutex_unlock(&ofproto->pin_mutex);
-
+ 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);
struct ofbundle *bundle;
int error;
- if (!clogged) {
- complete_operations(ofproto);
- }
-
if (mbridge_need_revalidate(ofproto->mbridge)) {
ofproto->backer->need_revalidate = REV_RECONFIGURE;
ovs_rwlock_wrlock(&ofproto->ml->rwlock);
struct ofport_dpif *ofport;
struct ofbundle *bundle;
- if (!clogged && !list_is_empty(&ofproto->completions)) {
- poll_immediate_wake();
- }
-
if (ofproto_get_flow_restore_wait()) {
return;
}
bundle_send_learning_packets(struct ofbundle *bundle)
{
struct ofproto_dpif *ofproto = bundle->ofproto;
+ struct ofpbuf *learning_packet;
int error, n_packets, n_errors;
struct mac_entry *e;
+ struct list packets;
- error = n_packets = n_errors = 0;
+ list_init(&packets);
ovs_rwlock_rdlock(&ofproto->ml->rwlock);
LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
if (e->port.p != bundle) {
- struct ofpbuf *learning_packet;
- struct ofport_dpif *port;
void *port_void;
- int ret;
- /* The assignment to "port" is unnecessary but makes "grep"ing for
- * struct ofport_dpif more effective. */
learning_packet = bond_compose_learning_packet(bundle->bond,
e->mac, e->vlan,
&port_void);
- port = port_void;
- ret = send_packet(port, learning_packet);
- ofpbuf_delete(learning_packet);
- if (ret) {
- error = ret;
- n_errors++;
- }
- n_packets++;
+ learning_packet->private_p = port_void;
+ list_push_back(&packets, &learning_packet->list_node);
}
}
ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ error = n_packets = n_errors = 0;
+ LIST_FOR_EACH (learning_packet, list_node, &packets) {
+ int ret;
+
+ ret = send_packet(learning_packet->private_p, learning_packet);
+ if (ret) {
+ error = ret;
+ n_errors++;
+ }
+ n_packets++;
+ }
+ ofpbuf_list_delete(&packets);
+
if (n_errors) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning "
sset_find_and_delete(&ofproto->ghost_ports,
netdev_get_name(ofport->up.netdev));
ofproto->backer->need_revalidate = REV_RECONFIGURE;
- if (!ofport->is_tunnel) {
+ if (!ofport->is_tunnel && !netdev_vport_is_patch(ofport->up.netdev)) {
error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
if (!error) {
/* The caller is going to close ofport->up.netdev. If this is a
hash = flow_hash_in_wildcards(&miss->flow, &miss->xout.wc, 0);
return governor_should_install_flow(backer->governor, hash,
- list_size(&miss->packets));
+ miss->stats.n_packets);
}
/* Handles 'miss', which matches 'facet'. May add any required datapath
facet->byte_count += miss->stats.n_bytes;
facet->prev_byte_count += miss->stats.n_bytes;
- subfacet = subfacet_create(facet, miss);
want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
/* Don't install the flow if it's the result of the "userspace"
{
struct facet *facet;
- miss->ofproto->n_missed += list_size(&miss->packets);
+ miss->ofproto->n_missed += miss->stats.n_packets;
facet = facet_lookup_valid(miss->ofproto, &miss->flow);
if (!facet) {
}
}
-static void
-handle_sflow_upcall(struct dpif_backer *backer,
- const struct dpif_upcall *upcall)
-{
- struct ofproto_dpif *ofproto;
- union user_action_cookie cookie;
- struct flow flow;
- odp_port_t odp_in_port;
-
- if (xlate_receive(backer, upcall->packet, upcall->key, upcall->key_len,
- &flow, NULL, &ofproto, &odp_in_port)
- || !ofproto->sflow) {
- return;
- }
-
- memset(&cookie, 0, sizeof cookie);
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.sflow);
- dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
- odp_in_port, &cookie);
-}
-
-static void
-handle_flow_sample_upcall(struct dpif_backer *backer,
- const struct dpif_upcall *upcall)
-{
- struct ofproto_dpif *ofproto;
- union user_action_cookie cookie;
- struct flow flow;
-
- if (xlate_receive(backer, upcall->packet, upcall->key, upcall->key_len,
- &flow, NULL, &ofproto, NULL)
- || !ofproto->ipfix) {
- return;
- }
-
- memset(&cookie, 0, sizeof cookie);
- memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.flow_sample);
-
- /* The flow reflects exactly the contents of the packet. Sample
- * the packet using it. */
- dpif_ipfix_flow_sample(ofproto->ipfix, upcall->packet, &flow,
- cookie.flow_sample.collector_set_id,
- cookie.flow_sample.probability,
- cookie.flow_sample.obs_domain_id,
- cookie.flow_sample.obs_point_id);
-}
-
-static void
-handle_ipfix_upcall(struct dpif_backer *backer,
- const struct dpif_upcall *upcall)
-{
- struct ofproto_dpif *ofproto;
- struct flow flow;
-
- if (xlate_receive(backer, upcall->packet, upcall->key, upcall->key_len,
- &flow, NULL, &ofproto, NULL)
- || !ofproto->ipfix) {
- return;
- }
-
- /* The flow reflects exactly the contents of the packet. Sample
- * the packet using it. */
- dpif_ipfix_bridge_sample(ofproto->ipfix, upcall->packet, &flow);
-}
-
static void
handle_upcalls(struct dpif_backer *backer)
{
struct flow_miss_batch *fmb;
int n_processed;
- for (n_processed = 0; n_processed < FLOW_MISS_MAX_BATCH; n_processed++) {
- struct upcall *upcall = upcall_next(backer->udpif);
-
- if (!upcall) {
- break;
- }
-
- switch (upcall->type) {
- case SFLOW_UPCALL:
- handle_sflow_upcall(backer, &upcall->dpif_upcall);
- break;
-
- case FLOW_SAMPLE_UPCALL:
- handle_flow_sample_upcall(backer, &upcall->dpif_upcall);
- break;
-
- case IPFIX_UPCALL:
- handle_ipfix_upcall(backer, &upcall->dpif_upcall);
- break;
-
- case BAD_UPCALL:
- break;
-
- case MISS_UPCALL:
- NOT_REACHED();
- }
-
- upcall_destroy(upcall);
- }
-
for (n_processed = 0; n_processed < FLOW_MISS_MAX_BATCH; n_processed++) {
struct drop_key *drop_key = drop_key_next(backer->udpif);
if (!drop_key) {
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 rule_expire(struct rule_dpif *) OVS_REQUIRES(ofproto_mutex);
static void expire_subfacets(struct dpif_backer *, int dp_max_idle);
/* This function is called periodically by run(). Its job is to collect
/* Expire OpenFlow flows whose idle_timeout or hard_timeout
* has passed. */
- ovs_mutex_lock(&ofproto->up.expirable_mutex);
+ ovs_mutex_lock(&ofproto_mutex);
LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
&ofproto->up.expirable) {
rule_expire(rule_dpif_cast(rule));
}
- ovs_mutex_unlock(&ofproto->up.expirable_mutex);
+ ovs_mutex_unlock(&ofproto_mutex);
/* All outstanding data in existing flows has been accounted, so it's a
* good time to do bond rebalancing. */
run_fast_rl();
}
dpif_flow_dump_done(&dump);
-
- update_moving_averages(backer);
}
/* Calculates and returns the number of milliseconds of idle time after which
* pass made by update_stats(), because the former function never looks at
* uninstallable subfacets.
*/
- enum { BUCKET_WIDTH = ROUND_UP(100, TIME_UPDATE_INTERVAL) };
+ enum { BUCKET_WIDTH = 100 };
enum { N_BUCKETS = 5000 / BUCKET_WIDTH };
int buckets[N_BUCKETS] = { 0 };
int total, subtotal, bucket;
* then delete it entirely. */
static void
rule_expire(struct rule_dpif *rule)
+ OVS_REQUIRES(ofproto_mutex)
{
uint16_t idle_timeout, hard_timeout;
- long long int now;
- uint8_t reason;
+ long long int now = time_msec();
+ int reason;
- if (rule->up.pending) {
- /* We'll have to expire it later. */
- return;
- }
+ ovs_assert(!rule->up.pending);
- ovs_mutex_lock(&rule->up.timeout_mutex);
+ /* Has 'rule' expired? */
+ ovs_mutex_lock(&rule->up.mutex);
hard_timeout = rule->up.hard_timeout;
idle_timeout = rule->up.idle_timeout;
- ovs_mutex_unlock(&rule->up.timeout_mutex);
-
- /* Has 'rule' expired? */
- now = time_msec();
if (hard_timeout && now > rule->up.modified + hard_timeout * 1000) {
reason = OFPRR_HARD_TIMEOUT;
} else if (idle_timeout && now > rule->up.used + idle_timeout * 1000) {
reason = OFPRR_IDLE_TIMEOUT;
} else {
- return;
+ reason = -1;
}
+ ovs_mutex_unlock(&rule->up.mutex);
- if (!ovs_rwlock_trywrlock(&rule->up.evict)) {
+ if (reason >= 0) {
COVERAGE_INC(ofproto_dpif_expired);
-
- /* Get rid of the rule. */
ofproto_rule_expire(&rule->up, reason);
}
}
struct facet *facet;
struct match match;
+ COVERAGE_INC(facet_create);
facet = xzalloc(sizeof *facet);
facet->ofproto = miss->ofproto;
facet->used = miss->stats.used;
{
struct subfacet *subfacet, *next_subfacet;
+ COVERAGE_INC(facet_remove);
ovs_assert(!list_is_empty(&facet->subfacets));
/* First uninstall all of the subfacets to get final statistics. */
if (facet) {
struct ofproto_dpif *ofproto = facet->ofproto;
const struct ofpact *ofpacts;
+ struct rule_actions *actions;
struct rule_dpif *rule;
size_t ofpacts_len;
bool is_controller;
rule_dpif_lookup(ofproto, &facet->flow, NULL, &rule);
- ofpacts_len = rule->up.ofpacts_len;
- ofpacts = rule->up.ofpacts;
+ actions = rule_dpif_get_actions(rule);
+ rule_dpif_unref(rule);
+
+ ofpacts_len = actions->ofpacts_len;
+ ofpacts = actions->ofpacts;
is_controller = ofpacts_len > 0
&& ofpacts->type == OFPACT_CONTROLLER
&& ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len);
- rule_release(rule);
+ rule_actions_unref(actions);
+
return is_controller;
}
return false;
rule_dpif_lookup(facet->ofproto, &facet->flow, NULL, &rule);
xlate_in_init(&xin, facet->ofproto, &facet->flow, rule, 0, NULL);
xlate_actions(&xin, &xout);
- rule_release(rule);
+ rule_dpif_unref(rule);
ok = ofpbuf_equal(&facet->xout.odp_actions, &xout.odp_actions)
&& facet->xout.slow == xout.slow;
|| memcmp(&facet->xout.wc, &xout.wc, sizeof xout.wc)) {
facet_remove(facet);
xlate_out_uninit(&xout);
- rule_release(new_rule);
+ rule_dpif_unref(new_rule);
return false;
}
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);
+ ovs_mutex_unlock(&new_rule->up.mutex);
xlate_out_uninit(&xout);
- rule_release(new_rule);
+ rule_dpif_unref(new_rule);
return true;
}
}
rule_dpif_lookup(ofproto, flow, NULL, &rule);
- rule_credit_stats(rule, stats);
+ rule_dpif_credit_stats(rule, stats);
xlate_in_init(&xin, ofproto, flow, rule, stats->tcp_flags, NULL);
xin.resubmit_stats = stats;
xin.may_learn = may_learn;
xlate_actions_for_side_effects(&xin);
- rule_release(rule);
+ rule_dpif_unref(rule);
}
static void
}
void
-rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
+rule_dpif_credit_stats(struct rule_dpif *rule,
+ const struct dpif_flow_stats *stats)
{
ovs_mutex_lock(&rule->stats_mutex);
rule->packet_count += stats->n_packets;
rule->byte_count += stats->n_bytes;
- ofproto_rule_update_used(&rule->up, stats->used);
+ rule->up.used = MAX(rule->up.used, stats->used);
ovs_mutex_unlock(&rule->stats_mutex);
}
+
+bool
+rule_dpif_fail_open(const struct rule_dpif *rule)
+{
+ return rule->up.cr.priority == FAIL_OPEN_PRIORITY;
+}
+
+ovs_be64
+rule_dpif_get_flow_cookie(const struct rule_dpif *rule)
+ OVS_REQUIRES(rule->up.mutex)
+{
+ return rule->up.flow_cookie;
+}
+
+void
+rule_dpif_reduce_timeouts(struct rule_dpif *rule, uint16_t idle_timeout,
+ uint16_t hard_timeout)
+{
+ ofproto_rule_reduce_timeouts(&rule->up, idle_timeout, hard_timeout);
+}
+
+/* Returns 'rule''s actions. The caller owns a reference on the returned
+ * actions and must eventually release it (with rule_actions_unref()) to avoid
+ * a memory leak. */
+struct rule_actions *
+rule_dpif_get_actions(const struct rule_dpif *rule)
+{
+ return rule_get_actions(&rule->up);
+}
\f
/* Subfacets. */
subfacet_create(struct facet *facet, struct flow_miss *miss)
{
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;
uint32_t key_hash;
subfacet = xmalloc(sizeof *subfacet);
}
+ COVERAGE_INC(subfacet_create);
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->key = xmemdup(key, key_len);
subfacet->key_len = key_len;
subfacet->used = miss->stats.used;
subfacet->path = SF_NOT_INSTALLED;
subfacet->backer = backer;
- backer->subfacet_add_count++;
return subfacet;
}
subfacet_destroy__(struct subfacet *subfacet)
{
struct facet *facet = subfacet->facet;
- struct ofproto_dpif *ofproto = facet->ofproto;
-
- /* Update ofproto stats before uninstall the subfacet. */
- ofproto->backer->subfacet_del_count++;
+ COVERAGE_INC(subfacet_destroy);
subfacet_uninstall(subfacet);
hmap_remove(&subfacet->backer->subfacets, &subfacet->hmap_node);
list_remove(&subfacet->list_node);
flow->in_port.ofp_port);
}
- *rule = choose_miss_rule(port ? port->up.pp.config : 0, ofproto->miss_rule,
- ofproto->no_packet_in_rule);
- ovs_rwlock_rdlock(&(*rule)->up.evict);
+ choose_miss_rule(port ? port->up.pp.config : 0, ofproto->miss_rule,
+ ofproto->no_packet_in_rule, rule);
}
bool
rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
const struct flow *flow, struct flow_wildcards *wc,
uint8_t table_id, struct rule_dpif **rule)
- OVS_ACQ_RDLOCK((*rule)->up.evict)
{
- struct cls_rule *cls_rule;
+ const struct cls_rule *cls_rule;
struct classifier *cls;
bool frag;
}
*rule = rule_dpif_cast(rule_from_cls_rule(cls_rule));
- if (*rule && ovs_rwlock_tryrdlock(&(*rule)->up.evict)) {
- /* The rule is in the process of being removed. Best we can do is
- * pretend it isn't there. */
- *rule = NULL;
- }
+ rule_dpif_ref(*rule);
ovs_rwlock_unlock(&cls->rwlock);
return *rule != NULL;
/* Given a port configuration (specified as zero if there's no port), chooses
* which of 'miss_rule' and 'no_packet_in_rule' should be used in case of a
* flow table miss. */
-struct rule_dpif *
+void
choose_miss_rule(enum ofputil_port_config config, struct rule_dpif *miss_rule,
- struct rule_dpif *no_packet_in_rule)
+ struct rule_dpif *no_packet_in_rule, struct rule_dpif **rule)
{
- return config & OFPUTIL_PC_NO_PACKET_IN ? no_packet_in_rule : miss_rule;
+ *rule = config & OFPUTIL_PC_NO_PACKET_IN ? no_packet_in_rule : miss_rule;
+ rule_dpif_ref(*rule);
}
void
-rule_release(struct rule_dpif *rule)
- OVS_NO_THREAD_SAFETY_ANALYSIS
+rule_dpif_ref(struct rule_dpif *rule)
{
if (rule) {
- ovs_rwlock_unlock(&rule->up.evict);
+ ofproto_rule_ref(&rule->up);
+ }
+}
+
+void
+rule_dpif_unref(struct rule_dpif *rule)
+{
+ if (rule) {
+ ofproto_rule_unref(&rule->up);
}
}
static void
complete_operation(struct rule_dpif *rule)
+ OVS_REQUIRES(ofproto_mutex)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
ofproto->backer->need_revalidate = REV_FLOW_TABLE;
- if (clogged) {
- struct dpif_completion *c = xmalloc(sizeof *c);
- c->op = rule->up.pending;
- list_push_back(&ofproto->completions, &c->list_node);
- } else {
- ofoperation_complete(rule->up.pending, 0);
- }
+ ofoperation_complete(rule->up.pending, 0);
+}
+
+static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
+{
+ return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
}
static struct rule *
rule->packet_count = 0;
rule->byte_count = 0;
ovs_mutex_unlock(&rule->stats_mutex);
- complete_operation(rule);
return 0;
}
static void
-rule_destruct(struct rule *rule_)
+rule_insert(struct rule *rule_)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ complete_operation(rule);
+}
+
+static void
+rule_delete(struct rule *rule_)
+ OVS_REQUIRES(ofproto_mutex)
{
struct rule_dpif *rule = rule_dpif_cast(rule_);
complete_operation(rule);
+}
+
+static void
+rule_destruct(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
ovs_mutex_destroy(&rule->stats_mutex);
}
struct xlate_in xin;
dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
- rule_credit_stats(rule, &stats);
+ rule_dpif_credit_stats(rule, &stats);
xlate_in_init(&xin, ofproto, flow, rule, stats.tcp_flags, packet);
xin.resubmit_stats = &stats;
}
static void
-rule_modify_actions(struct rule *rule_)
+rule_modify_actions(struct rule *rule_, bool reset_counters)
+ OVS_REQUIRES(ofproto_mutex)
{
struct rule_dpif *rule = rule_dpif_cast(rule_);
+ if (reset_counters) {
+ ovs_mutex_lock(&rule->stats_mutex);
+ rule->packet_count = 0;
+ rule->byte_count = 0;
+ ovs_mutex_unlock(&rule->stats_mutex);
+ }
+
complete_operation(rule);
}
\f
static void
trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
{
+ struct rule_actions *actions;
+ ovs_be64 cookie;
+
ds_put_char_multiple(result, '\t', level);
if (!rule) {
ds_put_cstr(result, "No match\n");
return;
}
+ ovs_mutex_lock(&rule->up.mutex);
+ cookie = rule->up.flow_cookie;
+ ovs_mutex_unlock(&rule->up.mutex);
+
ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
- rule ? rule->up.table_id : 0, ntohll(rule->up.flow_cookie));
+ rule ? rule->up.table_id : 0, ntohll(cookie));
cls_rule_format(&rule->up.cr, result);
ds_put_char(result, '\n');
+ actions = rule_dpif_get_actions(rule);
+
ds_put_char_multiple(result, '\t', level);
ds_put_cstr(result, "OpenFlow ");
- ofpacts_format(rule->up.ofpacts, rule->up.ofpacts_len, result);
+ ofpacts_format(actions->ofpacts, actions->ofpacts_len, result);
ds_put_char(result, '\n');
+
+ rule_actions_unref(actions);
}
static void
ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
void *aux OVS_UNUSED)
{
- const struct dpif_backer *backer;
+ const struct dpif_backer *backer = NULL;
struct ofproto_dpif *ofproto;
struct ofpbuf odp_key, odp_mask;
struct ofpbuf *packet;
struct ds result;
struct flow flow;
+ struct simap port_names;
char *s;
packet = 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")) {
}
}
+ /* odp_flow can have its in_port specified as a name instead of port no.
+ * We do not yet know whether a given flow is a odp_flow or a br_flow.
+ * But, to know whether a flow is odp_flow through odp_flow_from_string(),
+ * we need to create a simap of name to port no. */
+ if (argc == 3) {
+ const char *dp_type;
+ if (!strncmp(argv[1], "ovs-", 4)) {
+ dp_type = argv[1] + 4;
+ } else {
+ dp_type = argv[1];
+ }
+ backer = shash_find_data(&all_dpif_backers, dp_type);
+ } else {
+ struct shash_node *node;
+ if (shash_count(&all_dpif_backers) == 1) {
+ node = shash_first(&all_dpif_backers);
+ backer = node->data;
+ }
+ }
+ if (backer && backer->dpif) {
+ struct dpif_port dpif_port;
+ struct dpif_port_dump port_dump;
+ DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, backer->dpif) {
+ simap_put(&port_names, dpif_port.name,
+ odp_to_u32(dpif_port.port_no));
+ }
+ }
+
/* Parse the flow and determine whether a datapath or
* 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], NULL, &odp_key, &odp_mask)) {
- /* If the odp_flow is the second argument,
- * the datapath name is the first argument. */
- if (argc == 3) {
- const char *dp_type;
- if (!strncmp(argv[1], "ovs-", 4)) {
- dp_type = argv[1] + 4;
- } else {
- dp_type = argv[1];
- }
- backer = shash_find_data(&all_dpif_backers, dp_type);
- if (!backer) {
- unixctl_command_reply_error(conn, "Cannot find datapath "
- "of this name");
- goto exit;
- }
- } else {
- /* No datapath name specified, so there should be only one
- * datapath. */
- struct shash_node *node;
- if (shash_count(&all_dpif_backers) != 1) {
- unixctl_command_reply_error(conn, "Must specify datapath "
- "name, there is more than one type of datapath");
- goto exit;
- }
- node = shash_first(&all_dpif_backers);
- backer = node->data;
+ 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");
+ goto exit;
}
if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, &flow,
ofpbuf_delete(packet);
ofpbuf_uninit(&odp_key);
ofpbuf_uninit(&odp_mask);
+ simap_destroy(&port_names);
}
static void
xlate_out_uninit(&trace.xout);
}
- rule_release(rule);
-}
-
-static void
-ofproto_dpif_clog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
-{
- clogged = true;
- unixctl_command_reply(conn, NULL);
-}
-
-static void
-ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
-{
- clogged = false;
- unixctl_command_reply(conn, NULL);
+ rule_dpif_unref(rule);
}
/* Runs a self-check of flow translations in 'ofproto'. Appends a message to
ds_destroy(&ds);
}
-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
dpif_show_backer(const struct dpif_backer *backer, struct ds *ds)
{
struct ofproto_dpif *ofproto;
struct shash ofproto_shash;
uint64_t n_hit, n_missed;
- long long int minutes;
size_t i;
n_hit = n_missed = 0;
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, "\thourly avg:", &backer->hourly);
- }
- if (minutes >= 60 * 24) {
- show_dp_rates(ds, "\tdaily avg:", &backer->daily);
- }
- show_dp_rates(ds, "\toverall avg:", &backer->lifetime);
-
shash_init(&ofproto_shash);
ofprotos = get_ofprotos(&ofproto_shash);
for (i = 0; i < shash_count(&ofproto_shash); i++) {
}
odp_flow_format(subfacet->key, subfacet->key_len,
- mask.data, mask.size, &ds, false);
+ mask.data, mask.size, NULL, &ds, false);
ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
subfacet->dp_packet_count, subfacet->dp_byte_count);
ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", 1, 1,
ofproto_unixctl_fdb_show, NULL);
- unixctl_command_register("ofproto/clog", "", 0, 0,
- ofproto_dpif_clog, NULL);
- unixctl_command_register("ofproto/unclog", "", 0, 0,
- ofproto_dpif_unclog, NULL);
unixctl_command_register("ofproto/self-check", "[bridge]", 0, 1,
ofproto_dpif_self_check, NULL);
unixctl_command_register("dpif/dump-dps", "", 0, 0,
}
}
-/* 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 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() - 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 ((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);
- }
-
- 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;
- }
-}
-
const struct ofproto_class ofproto_dpif_class = {
init,
enumerate_types,
NULL, /* rule_choose_table */
rule_alloc,
rule_construct,
+ rule_insert,
+ rule_delete,
rule_destruct,
rule_dealloc,
rule_get_stats,
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 */
};