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 rule_dpif {
ofproto_flow_mod(&ofproto->up, fm);
}
+/* Resets the modified time for 'rule' or an equivalent rule. If 'rule' is not
+ * in the classifier, but an equivalent rule is, unref 'rule' and ref the new
+ * rule. Otherwise if 'rule' is no longer installed in the classifier,
+ * reinstall it.
+ *
+ * Returns the rule whose modified time has been reset. */
+struct rule_dpif *
+ofproto_dpif_refresh_rule(struct rule_dpif *rule)
+{
+ return rule_dpif_cast(ofproto_refresh_rule(&rule->up));
+}
+
/* Appends 'pin' to the queue of "packet ins" to be sent to the controller.
* Takes ownership of 'pin' and pin->packet. */
void
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,
ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL,
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,
ofpbuf_data(&key), ofpbuf_size(&key), NULL, 0, NULL, 0, NULL);
return error;
}
-static bool
+static int
get_cfm_status(const struct ofport *ofport_,
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)) {
+ 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
get_bfd_status(struct ofport *ofport_, 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)) {
+ bfd_get_status(ofport->bfd, smap);
+ } else {
+ ret = NO_STATUS_CHANGE;
+ }
} else {
- return ENOENT;
+ ret = ENOENT;
}
+
+ return ret;
}
\f
/* Spanning Tree. */
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);
+ if (take_ref) {
+ rule_dpif_ref(*rule);
+ }
}
void
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) {
xlate_out_uninit(&trace.xout);
}
-
- rule_dpif_unref(rule);
}
/* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list
}
rule = rule_dpif_lookup_in_table(ofproto, TBL_INTERNAL, &match->flow,
- &match->wc);
+ &match->wc, false);
if (rule) {
- rule_dpif_unref(rule);
*rulep = &rule->up;
} else {
OVS_NOT_REACHED();