COVERAGE_DEFINE(ofproto_dpif_expired);
COVERAGE_DEFINE(packet_in_overflow);
-/* Number of implemented OpenFlow tables. */
-enum { N_TABLES = 255 };
-enum { TBL_INTERNAL = N_TABLES - 1 }; /* Used for internal hidden rules. */
-BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
+/* No bfd/cfm status change. */
+#define NO_STATUS_CHANGE -1
struct flow_miss;
struct dpif_flow_stats stats OVS_GUARDED;
};
+/* RULE_CAST() depends on this. */
+BUILD_ASSERT_DECL(offsetof(struct rule_dpif, up) == 0);
+
static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes,
long long int *used);
static struct rule_dpif *rule_dpif_cast(const struct rule *);
/* Work queues. */
struct guarded_list pins; /* Contains "struct ofputil_packet_in"s. */
+ struct seq *pins_seq; /* For notifying 'pins' reception. */
+ uint64_t pins_seqno;
};
/* All existing ofproto_dpif instances, indexed by ->up.name. */
free(CONST_CAST(void *, pin->up.packet));
free(pin);
}
+
+ /* Wakes up main thread for packet-in I/O. */
+ seq_change(ofproto->pins_seq);
}
/* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the
shash_add(&all_dpif_backers, type, backer);
+ backer->enable_recirc = check_recirc(backer);
+ backer->variable_length_userdata = check_variable_length_userdata(backer);
+ backer->max_mpls_depth = check_max_mpls_depth(backer);
+ backer->rid_pool = recirc_id_pool_create();
+
error = dpif_recv_set(backer->dpif, backer->recv_set_enable);
if (error) {
VLOG_ERR("failed to listen on datapath of type %s: %s",
close_dpif_backer(backer);
return error;
}
- backer->enable_recirc = check_recirc(backer);
- backer->variable_length_userdata = check_variable_length_userdata(backer);
- backer->max_mpls_depth = check_max_mpls_depth(backer);
- backer->rid_pool = recirc_id_pool_create();
if (backer->recv_set_enable) {
udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
return error;
}
-/* Tests whether 'backer''s datapath supports recirculation Only newer datapath
- * supports OVS_KEY_ATTR in OVS_ACTION_ATTR_USERSPACE actions. We need to
- * disable some features on older datapaths that don't support this feature.
+/* Tests whether 'backer''s datapath supports recirculation. Only newer
+ * datapaths support OVS_KEY_ATTR_RECIRC_ID in keys. We need to disable some
+ * features on older datapaths that don't support this feature.
*
* Returns false if 'backer' definitely does not support recirculation, true if
* it seems to support recirculation or if at least the error we get is
flow.dp_hash = 1;
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&key, &flow, 0);
+ odp_flow_key_from_flow(&key, &flow, NULL, 0);
- error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+ error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE,
ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL,
0, NULL);
if (error && error != EEXIST) {
switch (error) {
case 0:
- /* Variable-length userdata is supported.
- *
- * Purge received packets to avoid processing the nonsense packet we
- * sent to userspace, then report success. */
- dpif_recv_purge(backer->dpif);
return true;
case ERANGE:
flow_set_mpls_bos(&flow, n, 1);
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
- odp_flow_key_from_flow(&key, &flow, 0);
+ odp_flow_key_from_flow(&key, &flow, NULL, 0);
- error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+ error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE,
ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL, 0, NULL);
if (error && error != EEXIST) {
if (error != EINVAL) {
sset_init(&ofproto->port_poll_set);
ofproto->port_poll_errno = 0;
ofproto->change_seq = 0;
+ ofproto->pins_seq = seq_create();
+ ofproto->pins_seqno = seq_read(ofproto->pins_seq);
+
SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) {
struct iface_hint *iface_hint = node->data;
ofpbuf_clear(&ofpacts);
error = add_internal_miss_flow(ofproto, id++, &ofpacts,
- &ofproto->no_packet_in_rule);
+ &ofproto->no_packet_in_rule);
if (error) {
return error;
}
error = add_internal_miss_flow(ofproto, id++, &ofpacts,
- &ofproto->drop_frags_rule);
+ &ofproto->drop_frags_rule);
if (error) {
return error;
}
match_init_catchall(&match);
match_set_recirc_id(&match, 0);
- error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, &ofpacts,
+ error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, &ofpacts,
&unused_rulep);
if (error) {
return error;
*/
ofpbuf_clear(&ofpacts);
match_init_catchall(&match);
- error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, &ofpacts,
+ error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, &ofpacts,
&unused_rulep);
return error;
ovs_mutex_destroy(&ofproto->stats_mutex);
ovs_mutex_destroy(&ofproto->vsp_mutex);
+ seq_destroy(ofproto->pins_seq);
+
close_dpif_backer(ofproto->backer);
}
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
uint64_t new_seq, new_dump_seq;
- const bool enable_recirc = ofproto_dpif_get_enable_recirc(ofproto);
if (mbridge_need_revalidate(ofproto->mbridge)) {
ofproto->backer->need_revalidate = REV_RECONFIGURE;
ovs_rwlock_unlock(&ofproto->ml->rwlock);
}
+ /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during
+ * flow restore. Even though nothing is processed during flow restore,
+ * all queued 'pins' will be handled immediately when flow restore
+ * completes. */
+ ofproto->pins_seqno = seq_read(ofproto->pins_seq);
+
/* Do not perform any periodic activity required by 'ofproto' while
* waiting for flow restore to complete. */
if (!ofproto_get_flow_restore_wait()) {
/* All outstanding data in existing flows has been accounted, so it's a
* good time to do bond rebalancing. */
- if (enable_recirc && ofproto->has_bonded_bundles) {
+ if (ofproto->has_bonded_bundles) {
struct ofbundle *bundle;
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
- struct bond *bond = bundle->bond;
-
- if (bond && bond_may_recirc(bond, NULL, NULL)) {
- bond_recirculation_account(bond);
- if (bond_rebalance(bundle->bond)) {
- bond_update_post_recirc_rules(bond, true);
- }
+ if (bundle->bond) {
+ bond_rebalance(bundle->bond);
}
}
}
}
seq_wait(udpif_dump_seq(ofproto->backer->udpif), ofproto->dump_seq);
+ seq_wait(ofproto->pins_seq, ofproto->pins_seqno);
}
static void
return error;
}
-static bool
-get_cfm_status(const struct ofport *ofport_,
+static int
+get_cfm_status(const struct ofport *ofport_, bool force,
struct ofproto_cfm_status *status)
{
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ int ret = 0;
if (ofport->cfm) {
- status->faults = cfm_get_fault(ofport->cfm);
- status->flap_count = cfm_get_flap_count(ofport->cfm);
- status->remote_opstate = cfm_get_opup(ofport->cfm);
- status->health = cfm_get_health(ofport->cfm);
- cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
- return true;
+ if (cfm_check_status_change(ofport->cfm) || force) {
+ status->faults = cfm_get_fault(ofport->cfm);
+ status->flap_count = cfm_get_flap_count(ofport->cfm);
+ status->remote_opstate = cfm_get_opup(ofport->cfm);
+ status->health = cfm_get_health(ofport->cfm);
+ cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
+ } else {
+ ret = NO_STATUS_CHANGE;
+ }
} else {
- return false;
+ ret = ENOENT;
}
+
+ return ret;
}
static int
}
static int
-get_bfd_status(struct ofport *ofport_, struct smap *smap)
+get_bfd_status(struct ofport *ofport_, bool force, struct smap *smap)
{
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ int ret = 0;
if (ofport->bfd) {
- bfd_get_status(ofport->bfd, smap);
- return 0;
+ if (bfd_check_status_change(ofport->bfd) || force) {
+ bfd_get_status(ofport->bfd, smap);
+ } else {
+ ret = NO_STATUS_CHANGE;
+ }
} else {
- return ENOENT;
+ ret = ENOENT;
}
+
+ return ret;
}
\f
/* Spanning Tree. */
xin.resubmit_stats = &stats;
xlate_actions(&xin, &xout);
+ execute.actions = ofpbuf_data(&xout.odp_actions);
+ execute.actions_len = ofpbuf_size(&xout.odp_actions);
+ execute.packet = packet;
+ execute.md = pkt_metadata_from_flow(flow);
+ execute.needs_help = (xout.slow & SLOW_ACTION) != 0;
+
+ /* Fix up in_port. */
in_port = flow->in_port.ofp_port;
if (in_port == OFPP_NONE) {
in_port = OFPP_LOCAL;
}
- execute.actions = ofpbuf_data(&xout.odp_actions);
- execute.actions_len = ofpbuf_size(&xout.odp_actions);
- execute.packet = packet;
- execute.md.tunnel = flow->tunnel;
- execute.md.skb_priority = flow->skb_priority;
- execute.md.pkt_mark = flow->pkt_mark;
execute.md.in_port.odp_port = ofp_port_to_odp_port(ofproto, in_port);
- execute.needs_help = (xout.slow & SLOW_ACTION) != 0;
error = dpif_execute(ofproto->backer->dpif, &execute);
ovs_mutex_unlock(&rule->stats_mutex);
}
-bool
-rule_dpif_is_fail_open(const struct rule_dpif *rule)
-{
- return is_fail_open_rule(&rule->up);
-}
-
-bool
-rule_dpif_is_table_miss(const struct rule_dpif *rule)
-{
- return rule_is_table_miss(&rule->up);
-}
-
-bool
-rule_dpif_is_internal(const struct rule_dpif *rule)
-{
- return rule_is_internal(&rule->up);
-}
-
ovs_be64
rule_dpif_get_flow_cookie(const struct rule_dpif *rule)
OVS_REQUIRES(rule->up.mutex)
/* 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 *
+const struct rule_actions *
rule_dpif_get_actions(const struct rule_dpif *rule)
{
return rule_get_actions(&rule->up);
}
-static uint8_t
-rule_dpif_lookup__ (struct ofproto_dpif *ofproto, const struct flow *flow,
- struct flow_wildcards *wc, struct rule_dpif **rule)
+/* Lookup 'flow' in table 0 of 'ofproto''s classifier.
+ * If 'wc' is non-null, sets the fields that were relevant as part of
+ * the lookup. Returns the table_id where a match or miss occurred.
+ *
+ * The return value will be zero unless there was a miss and
+ * OFPTC11_TABLE_MISS_CONTINUE is in effect for the sequence of tables
+ * where misses occur.
+ *
+ * The rule is returned in '*rule', which is valid at least until the next
+ * RCU quiescent period. If the '*rule' needs to stay around longer,
+ * a non-zero 'take_ref' must be passed in to cause a reference to be taken
+ * on it before this returns. */
+uint8_t
+rule_dpif_lookup(struct ofproto_dpif *ofproto, struct flow *flow,
+ struct flow_wildcards *wc, struct rule_dpif **rule,
+ bool take_ref)
{
enum rule_dpif_lookup_verdict verdict;
enum ofputil_port_config config = 0;
- uint8_t table_id = TBL_INTERNAL;
+ uint8_t table_id;
+
+ if (ofproto_dpif_get_enable_recirc(ofproto)) {
+ /* Always exactly match recirc_id since datapath supports
+ * recirculation. */
+ if (wc) {
+ wc->masks.recirc_id = UINT32_MAX;
+ }
+
+ /* Start looking up from internal table for post recirculation flows
+ * or packets. We can also simply send all, including normal flows
+ * or packets to the internal table. They will not match any post
+ * recirculation rules except the 'catch all' rule that resubmit
+ * them to table 0.
+ *
+ * As an optimization, we send normal flows and packets to table 0
+ * directly, saving one table lookup. */
+ table_id = flow->recirc_id ? TBL_INTERNAL : 0;
+ } else {
+ table_id = 0;
+ }
verdict = rule_dpif_lookup_from_table(ofproto, flow, wc, true,
- &table_id, rule);
+ &table_id, rule, take_ref);
switch (verdict) {
case RULE_DPIF_LOOKUP_VERDICT_MATCH:
}
choose_miss_rule(config, ofproto->miss_rule,
- ofproto->no_packet_in_rule, rule);
+ ofproto->no_packet_in_rule, rule, take_ref);
return table_id;
}
-/* Lookup 'flow' in table 0 of 'ofproto''s classifier.
- * If 'wc' is non-null, sets the fields that were relevant as part of
- * the lookup. Returns the table_id where a match or miss occurred.
- *
- * The return value will be zero unless there was a miss and
- * O!-TC_TABLE_MISS_CONTINUE is in effect for the sequence of tables
- * where misses occur. */
-uint8_t
-rule_dpif_lookup(struct ofproto_dpif *ofproto, struct flow *flow,
- struct flow_wildcards *wc, struct rule_dpif **rule)
-{
- /* Set metadata to the value of recirc_id to speed up internal
- * rule lookup. */
- flow->metadata = htonll(flow->recirc_id);
- return rule_dpif_lookup__(ofproto, flow, wc, rule);
-}
-
+/* The returned rule is valid at least until the next RCU quiescent period.
+ * If the '*rule' needs to stay around longer, a non-zero 'take_ref' must be
+ * passed in to cause a reference to be taken on it before this returns. */
static struct rule_dpif *
rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, uint8_t table_id,
- const struct flow *flow, struct flow_wildcards *wc)
+ const struct flow *flow, struct flow_wildcards *wc,
+ bool take_ref)
{
struct classifier *cls = &ofproto->up.tables[table_id].cls;
const struct cls_rule *cls_rule;
}
rule = rule_dpif_cast(rule_from_cls_rule(cls_rule));
- rule_dpif_ref(rule);
+ if (take_ref) {
+ rule_dpif_ref(rule);
+ }
fat_rwlock_unlock(&cls->rwlock);
return rule;
* - RULE_OFPTC_TABLE_MISS_CONTROLLER if no rule was found and either:
* + 'honor_table_miss' is false
* + a table miss configuration specified that the packet should be
+ * sent to the controller in this case.
*
* - RULE_DPIF_LOOKUP_VERDICT_DROP if no rule was found, 'honor_table_miss'
* is true and a table miss configuration specified that the packet
*
* - RULE_DPIF_LOOKUP_VERDICT_DEFAULT if no rule was found,
* 'honor_table_miss' is true and a table miss configuration has
- * not been specified in this case. */
+ * not been specified in this case.
+ *
+ * The rule is returned in '*rule', which is valid at least until the next
+ * RCU quiescent period. If the '*rule' needs to stay around longer,
+ * a non-zero 'take_ref' must be passed in to cause a reference to be taken
+ * on it before this returns. */
enum rule_dpif_lookup_verdict
rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
const struct flow *flow,
struct flow_wildcards *wc,
bool honor_table_miss,
- uint8_t *table_id, struct rule_dpif **rule)
+ uint8_t *table_id, struct rule_dpif **rule,
+ bool take_ref)
{
uint8_t next_id;
next_id++, next_id += (next_id == TBL_INTERNAL))
{
*table_id = next_id;
- *rule = rule_dpif_lookup_in_table(ofproto, *table_id, flow, wc);
+ *rule = rule_dpif_lookup_in_table(ofproto, *table_id, flow, wc,
+ take_ref);
if (*rule) {
return RULE_DPIF_LOOKUP_VERDICT_MATCH;
} else if (!honor_table_miss) {
/* 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. */
+ * flow table miss.
+ *
+ * The rule is returned in '*rule', which is valid at least until the next
+ * RCU quiescent period. If the '*rule' needs to stay around longer,
+ * a reference must be taken on it (rule_dpif_ref()).
+ */
void
choose_miss_rule(enum ofputil_port_config config, struct rule_dpif *miss_rule,
- struct rule_dpif *no_packet_in_rule, struct rule_dpif **rule)
+ struct rule_dpif *no_packet_in_rule, struct rule_dpif **rule,
+ bool take_ref)
{
*rule = config & OFPUTIL_PC_NO_PACKET_IN ? no_packet_in_rule : miss_rule;
- rule_dpif_ref(*rule);
-}
-
-void
-rule_dpif_ref(struct rule_dpif *rule)
-{
- if (rule) {
- ofproto_rule_ref(&rule->up);
- }
-}
-
-void
-rule_dpif_unref(struct rule_dpif *rule)
-{
- if (rule) {
- ofproto_rule_unref(&rule->up);
+ if (take_ref) {
+ rule_dpif_ref(*rule);
}
}
struct trace_ctx {
struct xlate_out xout;
struct xlate_in xin;
+ const struct flow *key;
struct flow flow;
struct flow_wildcards wc;
struct ds *result;
static void
trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
{
- struct rule_actions *actions;
+ const struct rule_actions *actions;
ovs_be64 cookie;
ds_put_char_multiple(result, '\t', level);
{
ds_put_char_multiple(result, '\t', level);
ds_put_format(result, "%s: ", title);
- if (flow_equal(&trace->xin.flow, &trace->flow)) {
+ /* Do not report unchanged flows for resubmits. */
+ if ((level > 0 && flow_equal(&trace->xin.flow, &trace->flow))
+ || (level == 0 && flow_equal(&trace->xin.flow, trace->key))) {
ds_put_cstr(result, "unchanged");
} else {
flow_format(result, &trace->xin.flow);
ds_put_char_multiple(result, '\t', level);
ds_put_format(result, "%s: ", title);
flow_wildcards_or(&trace->wc, &trace->xout.wc, &trace->wc);
- match_init(&match, &trace->flow, &trace->wc);
+ match_init(&match, trace->key, &trace->wc);
match_format(&match, result, OFP_DEFAULT_PRIORITY);
ds_put_char(result, '\n');
}
if (ofpacts) {
rule = NULL;
} else {
- rule_dpif_lookup(ofproto, flow, &trace.wc, &rule);
+ rule_dpif_lookup(ofproto, flow, &trace.wc, &rule, false);
trace_format_rule(ds, 0, rule);
if (rule == ofproto->miss_rule) {
if (rule || ofpacts) {
trace.result = ds;
- trace.flow = *flow;
+ trace.key = flow; /* Original flow key, used for megaflow. */
+ trace.flow = *flow; /* May be modified by actions. */
xlate_in_init(&trace.xin, ofproto, flow, rule, ntohs(flow->tcp_flags),
packet);
if (ofpacts) {
xlate_out_uninit(&trace.xout);
}
-
- rule_dpif_unref(rule);
}
/* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list
ofproto_unixctl_dpif_dump_flows, NULL);
}
-
-/* Returns true if 'rule' is an internal rule, false otherwise. */
+/* Returns true if 'table' is the table used for internal rules,
+ * false otherwise. */
bool
-rule_is_internal(const struct rule *rule)
+table_is_internal(uint8_t table_id)
{
- return rule->table_id == TBL_INTERNAL;
+ return table_id == TBL_INTERNAL;
}
\f
/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
int
ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
- struct match *match, int priority,
+ const struct match *match, int priority,
const struct ofpbuf *ofpacts,
struct rule **rulep)
{
return error;
}
- rule = rule_dpif_lookup_in_table(ofproto, TBL_INTERNAL, &match->flow,
- &match->wc);
+ rule = rule_dpif_lookup_in_table(ofproto, TBL_INTERNAL, &fm.match.flow,
+ &fm.match.wc, false);
if (rule) {
- rule_dpif_unref(rule);
*rulep = &rule->up;
} else {
OVS_NOT_REACHED();