/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009-2015 Nicira, Inc.
* Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include "timeval.h"
#include "unaligned.h"
#include "unixctl.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
#include "bundles.h"
VLOG_DEFINE_THIS_MODULE(ofproto);
size_t n_fields);
static void oftable_remove_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex);
-static void oftable_remove_rule__(struct ofproto *, struct rule *)
- OVS_REQUIRES(ofproto_mutex);
/* A set of rules within a single OpenFlow table (oftable) that have the same
* values for the oftable's eviction_fields. A rule to be evicted, when one is
* (We can't do this immediately from ofopgroup_complete() because that holds
* ofproto_mutex, which rule_execute() needs released.) */
struct rule_execute {
- struct list list_node; /* In struct ofproto's "rule_executes" list. */
+ struct ovs_list list_node; /* In struct ofproto's "rule_executes" list. */
struct rule *rule; /* Owns a reference to the rule. */
ofp_port_t in_port;
struct ofpbuf *packet; /* Owns the packet. */
struct hmap_node hmap_node OVS_GUARDED_BY(ofproto_mutex);
/* In 'dead_cookies' list when removed from hmap. */
- struct list list_node;
+ struct ovs_list list_node;
} u;
/* Key. */
static void learned_cookies_inc(struct ofproto *, const struct rule_actions *)
OVS_REQUIRES(ofproto_mutex);
static void learned_cookies_dec(struct ofproto *, const struct rule_actions *,
- struct list *dead_cookies)
+ struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex);
-static void learned_cookies_flush(struct ofproto *, struct list *dead_cookies)
+static void learned_cookies_flush(struct ofproto *, struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex);
/* ofport. */
/* rule. */
static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
static bool rule_is_readonly(const struct rule *);
+static void ofproto_rule_remove__(struct ofproto *, struct rule *)
+ OVS_REQUIRES(ofproto_mutex);
/* The source of a flow_mod request, in the code that processes flow_mods.
*
connmgr_get_snoops(ofproto->connmgr, snoops);
}
-static void
-ofproto_rule_delete__(struct rule *rule, uint8_t reason)
- OVS_REQUIRES(ofproto_mutex)
-{
- struct rule_collection rules;
-
- rules.rules = rules.stub;
- rules.n = 1;
- rules.stub[0] = rule;
- delete_flows__(&rules, reason, NULL);
-}
-
/* Deletes 'rule' from 'ofproto'.
*
* Within an ofproto implementation, this function allows an ofproto
* switch is being deleted and any OpenFlow channels have been or soon will
* be killed. */
ovs_mutex_lock(&ofproto_mutex);
- oftable_remove_rule__(ofproto, rule);
+ oftable_remove_rule(rule);
ofproto->ofproto_class->rule_delete(rule);
ovs_mutex_unlock(&ofproto_mutex);
}
ovs_mutex_lock(&ofproto_mutex);
OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+ struct rule_collection rules;
struct rule *rule;
if (table->flags & OFTABLE_HIDDEN) {
continue;
}
- CLS_FOR_EACH_SAFE (rule, cr, &table->cls) {
- ofproto_rule_delete__(rule, OFPRR_DELETE);
+ rule_collection_init(&rules);
+
+ CLS_FOR_EACH (rule, cr, &table->cls) {
+ rule_collection_add(&rules, rule);
}
+ delete_flows__(&rules, OFPRR_DELETE, NULL);
+ rule_collection_destroy(&rules);
}
/* XXX: Concurrent handler threads may insert new learned flows based on
* learn actions of the now deleted flows right after we release
OVS_EXCLUDED(ofproto_mutex)
{
struct rule_execute *e, *next;
- struct list executes;
+ struct ovs_list executes;
guarded_list_pop_all(&ofproto->rule_executes, &executes);
LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
destroy_rule_executes(struct ofproto *ofproto)
{
struct rule_execute *e, *next;
- struct list executes;
+ struct ovs_list executes;
guarded_list_pop_all(&ofproto->rule_executes, &executes);
LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
static void
learned_cookies_update_one__(struct ofproto *ofproto,
const struct ofpact_learn *learn,
- int delta, struct list *dead_cookies)
+ int delta, struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex)
{
uint32_t hash = hash_learned_cookie(learn->cookie, learn->table_id);
static void
learned_cookies_update__(struct ofproto *ofproto,
const struct rule_actions *actions,
- int delta, struct list *dead_cookies)
+ int delta, struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex)
{
if (actions->has_learn_with_delete) {
static void
learned_cookies_dec(struct ofproto *ofproto,
const struct rule_actions *actions,
- struct list *dead_cookies)
+ struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex)
{
learned_cookies_update__(ofproto, actions, -1, dead_cookies);
}
static void
-learned_cookies_flush(struct ofproto *ofproto, struct list *dead_cookies)
+learned_cookies_flush(struct ofproto *ofproto, struct ovs_list *dead_cookies)
OVS_REQUIRES(ofproto_mutex)
{
struct learned_cookie *c, *next;
atomic_read_relaxed(&ofproto->tables[i].miss_config, &f->miss_config);
f->max_entries = 1000000;
- bitmap_set_multiple(f->nonmiss.next, i + 1,
- ofproto->n_tables - (i + 1), true);
+ bool more_tables = false;
+ for (int j = i + 1; j < ofproto->n_tables; j++) {
+ if (!(ofproto->tables[j].flags & OFTABLE_HIDDEN)) {
+ bitmap_set1(f->nonmiss.next, j);
+ more_tables = true;
+ }
+ }
f->nonmiss.instructions = (1u << N_OVS_INSTRUCTIONS) - 1;
- if (i == ofproto->n_tables - 1) {
+ if (!more_tables) {
f->nonmiss.instructions &= ~(1u << OVSINST_OFPIT11_GOTO_TABLE);
}
f->nonmiss.write.ofpacts = (UINT64_C(1) << N_OFPACTS) - 1;
s->table_id = i;
s->active_count = classifier_count(cls);
+ if (i == 0) {
+ s->active_count -= connmgr_count_hidden_rules(
+ ofproto->connmgr);
+ }
}
} else {
stats = NULL;
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_table_features *features;
- struct list replies;
+ struct ovs_list replies;
struct ofpbuf msg;
size_t i;
}
static void
-append_port_stat(struct ofport *port, struct list *replies)
+append_port_stat(struct ofport *port, struct ovs_list *replies)
{
struct ofputil_port_stats ops = { .port_no = port->pp.port_no };
static void
handle_port_request(struct ofconn *ofconn,
const struct ofp_header *request, ofp_port_t port_no,
- void (*cb)(struct ofport *, struct list *replies))
+ void (*cb)(struct ofport *, struct ovs_list *replies))
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofport *port;
- struct list replies;
+ struct ovs_list replies;
ofpmp_init(&replies, request);
if (port_no != OFPP_ANY) {
}
static void
-append_port_desc(struct ofport *port, struct list *replies)
+append_port_desc(struct ofport *port, struct ovs_list *replies)
{
ofputil_append_port_desc_stats_reply(&port->pp, replies);
}
struct ofputil_flow_stats_request fsr;
struct rule_criteria criteria;
struct rule_collection rules;
- struct list replies;
+ struct ovs_list replies;
enum ofperr error;
size_t i;
struct queue_stats_cbdata {
struct ofport *ofport;
- struct list replies;
+ struct ovs_list replies;
long long int now;
};
return error;
}
-static bool
-should_evict_a_rule(struct oftable *table, unsigned int extra_space)
- OVS_REQUIRES(ofproto_mutex)
- OVS_NO_THREAD_SAFETY_ANALYSIS
-{
- return classifier_count(&table->cls) + extra_space > table->max_flows;
-}
-
static enum ofperr
evict_rules_from_table(struct oftable *table, unsigned int extra_space)
OVS_REQUIRES(ofproto_mutex)
{
- while (should_evict_a_rule(table, extra_space)) {
+ enum ofperr error = 0;
+ struct rule_collection rules;
+ unsigned int count = classifier_count(&table->cls) + extra_space;
+ unsigned int max_flows = table->max_flows;
+
+ rule_collection_init(&rules);
+
+ while (count-- > max_flows) {
struct rule *rule;
if (!choose_rule_to_evict(table, &rule)) {
- return OFPERR_OFPFMFC_TABLE_FULL;
+ error = OFPERR_OFPFMFC_TABLE_FULL;
+ break;
} else {
- ofproto_rule_delete__(rule, OFPRR_EVICTION);
+ eviction_group_remove_rule(rule);
+ rule_collection_add(&rules, rule);
}
}
+ delete_flows__(&rules, OFPRR_EVICTION, NULL);
+ rule_collection_destroy(&rules);
- return 0;
+ return error;
}
/* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT
meter_insert_rule(rule);
}
- classifier_insert(&table->cls, CONST_CAST(struct cls_rule *, &rule->cr));
+ classifier_defer(&table->cls);
+ classifier_insert(&table->cls, &rule->cr);
error = ofproto->ofproto_class->rule_insert(rule);
if (error) {
ofproto_rule_unref(rule);
return error;
}
+ classifier_publish(&table->cls);
+
learned_cookies_inc(ofproto, actions);
if (minimask_get_vid_mask(&rule->cr.match.mask) == VLAN_VID_MASK) {
const struct flow_mod_requester *req)
OVS_REQUIRES(ofproto_mutex)
{
- struct list dead_cookies = LIST_INITIALIZER(&dead_cookies);
+ struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
enum nx_flow_update_event event;
size_t i;
OVS_REQUIRES(ofproto_mutex)
{
if (rules->n) {
- struct list dead_cookies = LIST_INITIALIZER(&dead_cookies);
+ struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
struct ofproto *ofproto = rules->rules[0]->ofproto;
+ struct rule *rule, *next;
size_t i;
- for (i = 0; i < rules->n; i++) {
- struct rule *rule = rules->rules[i];
- const struct rule_actions *actions = rule_get_actions(rule);
+ for (i = 0, next = rules->rules[0];
+ rule = next, next = (++i < rules->n) ? rules->rules[i] : NULL,
+ rule; ) {
+ struct classifier *cls = &ofproto->tables[rule->table_id].cls;
+ uint8_t next_table = next ? next->table_id : UINT8_MAX;
ofproto_rule_send_removed(rule, reason);
ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason,
req ? req->ofconn : NULL, req ? req->xid : 0,
NULL);
- oftable_remove_rule(rule);
+
+ if (next_table == rule->table_id) {
+ classifier_defer(cls);
+ }
+ classifier_remove(cls, &rule->cr);
+ if (next_table != rule->table_id) {
+ classifier_publish(cls);
+ }
+ ofproto_rule_remove__(ofproto, rule);
+
ofproto->ofproto_class->rule_delete(rule);
- learned_cookies_dec(ofproto, actions, &dead_cookies);
+ learned_cookies_dec(ofproto, rule_get_actions(rule),
+ &dead_cookies);
}
learned_cookies_flush(ofproto, &dead_cookies);
ofmonitor_flush(ofproto->connmgr);
error = collect_rules_loose(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- if (!error && rules.n > 0) {
+ if (!error) {
delete_flows__(&rules, fm->delete_reason, req);
}
rule_collection_destroy(&rules);
error = collect_rules_strict(ofproto, &criteria, &rules);
rule_criteria_destroy(&criteria);
- if (!error && rules.n > 0) {
+ if (!error) {
delete_flows__(&rules, fm->delete_reason, req);
}
rule_collection_destroy(&rules);
ofproto_rule_expire(struct rule *rule, uint8_t reason)
OVS_REQUIRES(ofproto_mutex)
{
- ofproto_rule_delete__(rule, reason);
+ struct rule_collection rules;
+
+ rules.rules = rules.stub;
+ rules.n = 1;
+ rules.stub[0] = rule;
+ delete_flows__(&rules, reason, NULL);
}
/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
static void
ofproto_compose_flow_refresh_update(const struct rule *rule,
enum nx_flow_monitor_flags flags,
- struct list *msgs)
+ struct ovs_list *msgs)
OVS_REQUIRES(ofproto_mutex)
{
const struct rule_actions *actions;
void
ofmonitor_compose_refresh_updates(struct rule_collection *rules,
- struct list *msgs)
+ struct ovs_list *msgs)
OVS_REQUIRES(ofproto_mutex)
{
size_t i;
struct ofmonitor **monitors;
size_t n_monitors, allocated_monitors;
struct rule_collection rules;
- struct list replies;
+ struct ovs_list replies;
enum ofperr error;
struct ofpbuf b;
size_t i;
*/
struct meter {
long long int created; /* Time created. */
- struct list rules; /* List of "struct rule_dpif"s. */
+ struct ovs_list rules; /* List of "struct rule_dpif"s. */
ofproto_meter_id provider_meter_id;
uint16_t flags; /* Meter flags. */
uint16_t n_bands; /* Number of meter bands. */
}
}
}
- if (rules.n > 0) {
- delete_flows__(&rules, OFPRR_METER_DELETE, NULL);
- }
+ delete_flows__(&rules, OFPRR_METER_DELETE, NULL);
/* Delete the meters. */
meter_delete(ofproto, first, last);
enum ofptype type)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct list replies;
+ struct ovs_list replies;
uint64_t bands_stub[256 / 8];
struct ofpbuf bands;
uint32_t meter_id, first, last;
}
static void
-append_group_stats(struct ofgroup *group, struct list *replies)
+append_group_stats(struct ofgroup *group, struct ovs_list *replies)
{
struct ofputil_group_stats ogs;
const struct ofproto *ofproto = group->ofproto;
static void
handle_group_request(struct ofconn *ofconn,
const struct ofp_header *request, uint32_t group_id,
- void (*cb)(struct ofgroup *, struct list *replies))
+ void (*cb)(struct ofgroup *, struct ovs_list *replies))
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofgroup *group;
- struct list replies;
+ struct ovs_list replies;
ofpmp_init(&replies, request);
if (group_id == OFPG_ALL) {
}
static void
-append_group_desc(struct ofgroup *group, struct list *replies)
+append_group_desc(struct ofgroup *group, struct ovs_list *replies)
{
struct ofputil_group_desc gds;
struct ofpbuf *buf;
struct ofputil_bundle_ctrl_msg reply;
+ error = reject_slave_controller(ofconn);
+ if (error) {
+ return error;
+ }
+
error = ofputil_decode_bundle_ctrl(oh, &bctrl);
if (error) {
return error;
enum ofperr error;
struct ofputil_bundle_add_msg badd;
+ error = reject_slave_controller(ofconn);
+ if (error) {
+ return error;
+ }
+
error = ofputil_decode_bundle_add(oh, &badd);
if (error) {
return error;
oftable_init(struct oftable *table)
{
memset(table, 0, sizeof *table);
- classifier_init(&table->cls, flow_segment_u32s);
+ classifier_init(&table->cls, flow_segment_u64s);
table->max_flows = UINT_MAX;
atomic_init(&table->miss_config, OFPUTIL_TABLE_MISS_DEFAULT);
}
}
-/* Removes 'rule' from the oftable that contains it. */
+/* Removes 'rule' from the ofproto data structures AFTER caller has removed
+ * it from the classifier. */
static void
-oftable_remove_rule__(struct ofproto *ofproto, struct rule *rule)
+ofproto_rule_remove__(struct ofproto *ofproto, struct rule *rule)
OVS_REQUIRES(ofproto_mutex)
{
- struct classifier *cls = &ofproto->tables[rule->table_id].cls;
-
- classifier_remove(cls, CONST_CAST(struct cls_rule *, &rule->cr));
-
cookies_remove(ofproto, rule);
eviction_group_remove_rule(rule);
oftable_remove_rule(struct rule *rule)
OVS_REQUIRES(ofproto_mutex)
{
- oftable_remove_rule__(rule->ofproto, rule);
+ struct classifier *cls = &rule->ofproto->tables[rule->table_id].cls;
+
+ if (classifier_remove(cls, &rule->cr)) {
+ ofproto_rule_remove__(rule->ofproto, rule);
+ }
}
\f
/* unixctl commands. */