static bool choose_rule_to_evict(struct oftable *table, struct rule **rulep);
static void ofproto_evict(struct ofproto *) OVS_EXCLUDED(ofproto_mutex);
-static uint32_t rule_eviction_priority(struct rule *);
+static uint32_t rule_eviction_priority(struct ofproto *ofproto, struct rule *);
static void eviction_group_add_rule(struct rule *);
static void eviction_group_remove_rule(struct rule *);
struct ovs_mutex ofproto_mutex = OVS_MUTEX_INITIALIZER;
unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT;
-enum ofproto_flow_miss_model flow_miss_model = OFPROTO_HANDLE_MISS_AUTO;
size_t n_handlers, n_revalidators;
ofproto_flow_limit = limit;
}
-/* Sets the path for handling flow misses. */
-void
-ofproto_set_flow_miss_model(unsigned model)
-{
- flow_miss_model = model;
-}
-
/* If forward_bpdu is true, the NORMAL action will forward frames with
* reserved (e.g. STP) destination Ethernet addresses. if forward_bpdu is false,
* the NORMAL action will drop these frames. */
ofproto_flow_mod(struct ofproto *ofproto, struct ofputil_flow_mod *fm)
OVS_EXCLUDED(ofproto_mutex)
{
+ /* Optimize for the most common case of a repeated learn action.
+ * If an identical flow already exists we only need to update its
+ * 'modified' time. */
+ if (fm->command == OFPFC_MODIFY_STRICT && fm->table_id != OFPTT_ALL
+ && !(fm->flags & OFPUTIL_FF_RESET_COUNTS)) {
+ struct oftable *table = &ofproto->tables[fm->table_id];
+ struct cls_rule match_rule;
+ struct rule *rule;
+ bool done = false;
+
+ cls_rule_init(&match_rule, &fm->match, fm->priority);
+ fat_rwlock_rdlock(&table->cls.rwlock);
+ rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls,
+ &match_rule));
+ if (rule) {
+ /* Reading many of the rule fields and writing on 'modified'
+ * requires the rule->mutex. Also, rule->actions may change
+ * if rule->mutex is not held. */
+ ovs_mutex_lock(&rule->mutex);
+ if (rule->idle_timeout == fm->idle_timeout
+ && rule->hard_timeout == fm->hard_timeout
+ && rule->flags == (fm->flags & OFPUTIL_FF_STATE)
+ && (!fm->modify_cookie || (fm->new_cookie == rule->flow_cookie))
+ && ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
+ rule->actions->ofpacts,
+ rule->actions->ofpacts_len)) {
+ /* Rule already exists and need not change, only update the
+ modified timestamp. */
+ rule->modified = time_msec();
+ done = true;
+ }
+ ovs_mutex_unlock(&rule->mutex);
+ }
+ fat_rwlock_unlock(&table->cls.rwlock);
+
+ if (done) {
+ return 0;
+ }
+ }
+
return handle_flow_mod__(ofproto, NULL, fm, NULL);
}
if (error) {
goto error;
}
- connmgr_send_port_status(p->connmgr, pp, OFPPR_ADD);
+ connmgr_send_port_status(p->connmgr, NULL, pp, OFPPR_ADD);
return;
error:
static void
ofport_remove(struct ofport *ofport)
{
- connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->pp,
+ connmgr_send_port_status(ofport->ofproto->connmgr, NULL, &ofport->pp,
OFPPR_DELETE);
ofport_destroy(ofport);
}
memcpy(port->pp.hw_addr, pp->hw_addr, ETH_ADDR_LEN);
port->pp.config = ((port->pp.config & ~OFPUTIL_PC_PORT_DOWN)
| (pp->config & OFPUTIL_PC_PORT_DOWN));
- port->pp.state = pp->state;
+ port->pp.state = ((port->pp.state & ~OFPUTIL_PS_LINK_DOWN)
+ | (pp->state & OFPUTIL_PS_LINK_DOWN));
port->pp.curr = pp->curr;
port->pp.advertised = pp->advertised;
port->pp.supported = pp->supported;
port->pp.curr_speed = pp->curr_speed;
port->pp.max_speed = pp->max_speed;
- connmgr_send_port_status(port->ofproto->connmgr, &port->pp, OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, NULL,
+ &port->pp, OFPPR_MODIFY);
}
/* Update OpenFlow 'state' in 'port' and notify controller. */
{
if (port->pp.state != state) {
port->pp.state = state;
- connmgr_send_port_status(port->ofproto->connmgr, &port->pp,
- OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, NULL,
+ &port->pp, OFPPR_MODIFY);
}
}
}
static void
-update_port_config(struct ofport *port,
+update_port_config(struct ofconn *ofconn, struct ofport *port,
enum ofputil_port_config config,
enum ofputil_port_config mask)
{
- enum ofputil_port_config old_config = port->pp.config;
- enum ofputil_port_config toggle;
+ enum ofputil_port_config toggle = (config ^ port->pp.config) & mask;
- toggle = (config ^ port->pp.config) & mask;
- if (toggle & OFPUTIL_PC_PORT_DOWN) {
- if (config & OFPUTIL_PC_PORT_DOWN) {
- netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL);
- } else {
- netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL);
- }
+ if (toggle & OFPUTIL_PC_PORT_DOWN
+ && (config & OFPUTIL_PC_PORT_DOWN
+ ? netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL)
+ : netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL))) {
+ /* We tried to bring the port up or down, but it failed, so don't
+ * update the "down" bit. */
toggle &= ~OFPUTIL_PC_PORT_DOWN;
}
- port->pp.config ^= toggle;
- if (port->pp.config != old_config) {
+ if (toggle) {
+ enum ofputil_port_config old_config = port->pp.config;
+ port->pp.config ^= toggle;
port->ofproto->ofproto_class->port_reconfigured(port, old_config);
+ connmgr_send_port_status(port->ofproto->connmgr, ofconn, &port->pp,
+ OFPPR_MODIFY);
}
}
} else if (!eth_addr_equals(port->pp.hw_addr, pm.hw_addr)) {
return OFPERR_OFPPMFC_BAD_HW_ADDR;
} else {
- update_port_config(port, pm.config, pm.mask);
+ update_port_config(ofconn, port, pm.config, pm.mask);
if (pm.advertise) {
netdev_set_advertisements(port->netdev, pm.advertise);
}
fs.idle_timeout = rule->idle_timeout;
fs.hard_timeout = rule->hard_timeout;
created = rule->created;
- used = rule->used;
modified = rule->modified;
actions = rule_get_actions__(rule);
flags = rule->flags;
ovs_mutex_unlock(&rule->mutex);
+ ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
+ &fs.byte_count, &used);
+
minimatch_expand(&rule->cr.match, &fs.match);
fs.table_id = rule->table_id;
calc_duration(created, now, &fs.duration_sec, &fs.duration_nsec);
fs.priority = rule->cr.priority;
fs.idle_age = age_secs(now - used);
fs.hard_age = age_secs(now - modified);
- ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
- &fs.byte_count);
fs.ofpacts = actions->ofpacts;
fs.ofpacts_len = actions->ofpacts_len;
{
uint64_t packet_count, byte_count;
struct rule_actions *actions;
- long long int created;
+ long long int created, used;
- rule->ofproto->ofproto_class->rule_get_stats(rule,
- &packet_count, &byte_count);
+ rule->ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
+ &byte_count, &used);
ovs_mutex_lock(&rule->mutex);
actions = rule_get_actions__(rule);
struct rule *rule = rules.rules[i];
uint64_t packet_count;
uint64_t byte_count;
+ long long int used;
ofproto->ofproto_class->rule_get_stats(rule, &packet_count,
- &byte_count);
+ &byte_count, &used);
if (packet_count == UINT64_MAX) {
unknown_packets = true;
ovs_refcount_init(&rule->ref_count);
rule->pending = NULL;
rule->flow_cookie = fm->new_cookie;
- rule->created = rule->modified = rule->used = time_msec();
+ rule->created = rule->modified = time_msec();
ovs_mutex_init(&rule->mutex);
ovs_mutex_lock(&rule->mutex);
OVS_REQUIRES(ofproto_mutex)
{
struct ofputil_flow_removed fr;
+ long long int used;
if (ofproto_rule_is_hidden(rule) ||
!(rule->flags & OFPUTIL_FF_SEND_FLOW_REM)) {
fr.hard_timeout = rule->hard_timeout;
ovs_mutex_unlock(&rule->mutex);
rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
- &fr.byte_count);
+ &fr.byte_count, &used);
connmgr_send_flow_removed(rule->ofproto->connmgr, &fr);
}
if (!op->error) {
long long int now = time_msec();
+ ovs_mutex_lock(&rule->mutex);
rule->modified = now;
if (op->type == OFOPERATION_REPLACE) {
- rule->created = rule->used = now;
+ rule->created = now;
}
+ ovs_mutex_unlock(&rule->mutex);
} else {
ofproto_rule_change_cookie(ofproto, rule, op->flow_cookie);
ovs_mutex_lock(&rule->mutex);
/* Returns an eviction priority for 'rule'. The return value should be
* interpreted so that higher priorities make a rule more attractive candidates
- * for eviction. */
+ * for eviction.
+ * Called only if have a timeout. */
static uint32_t
-rule_eviction_priority(struct rule *rule)
+rule_eviction_priority(struct ofproto *ofproto, struct rule *rule)
OVS_REQUIRES(ofproto_mutex)
{
- long long int hard_expiration;
- long long int idle_expiration;
- long long int expiration;
+ long long int expiration = LLONG_MAX;
+ long long int modified;
uint32_t expiration_offset;
- /* Calculate time of expiration. */
+ /* 'modified' needs protection even when we hold 'ofproto_mutex'. */
ovs_mutex_lock(&rule->mutex);
- hard_expiration = (rule->hard_timeout
- ? rule->modified + rule->hard_timeout * 1000
- : LLONG_MAX);
- idle_expiration = (rule->idle_timeout
- ? rule->used + rule->idle_timeout * 1000
- : LLONG_MAX);
- expiration = MIN(hard_expiration, idle_expiration);
+ modified = rule->modified;
ovs_mutex_unlock(&rule->mutex);
+
+ if (rule->hard_timeout) {
+ expiration = modified + rule->hard_timeout * 1000;
+ }
+ if (rule->idle_timeout) {
+ uint64_t packets, bytes;
+ long long int used;
+ long long int idle_expiration;
+
+ ofproto->ofproto_class->rule_get_stats(rule, &packets, &bytes, &used);
+ idle_expiration = used + rule->idle_timeout * 1000;
+ expiration = MIN(expiration, idle_expiration);
+ }
+
if (expiration == LLONG_MAX) {
return 0;
}
struct oftable *table = &ofproto->tables[rule->table_id];
bool has_timeout;
- ovs_mutex_lock(&rule->mutex);
+ /* Timeouts may be modified only when holding 'ofproto_mutex'. We have it
+ * so no additional protection is needed. */
has_timeout = rule->hard_timeout || rule->idle_timeout;
- ovs_mutex_unlock(&rule->mutex);
if (table->eviction_fields && has_timeout) {
struct eviction_group *evg;
rule->eviction_group = evg;
heap_insert(&evg->rules, &rule->evg_node,
- rule_eviction_priority(rule));
+ rule_eviction_priority(ofproto, rule));
eviction_group_resized(table, evg);
}
}