+static bool
+facet_check_consistency(struct facet *facet)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+
+ struct rule_dpif *rule;
+ struct subfacet *subfacet;
+ bool may_log = false;
+ bool ok;
+
+ /* Check the rule for consistency. */
+ rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
+ if (!rule) {
+ if (!VLOG_DROP_WARN(&rl)) {
+ char *s = flow_to_string(&facet->flow);
+ VLOG_WARN("%s: facet should not exist", s);
+ free(s);
+ }
+ return false;
+ } else if (rule != facet->rule) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ ok = false;
+ if (may_log) {
+ struct ds s;
+
+ ds_init(&s);
+ flow_format(&s, &facet->flow);
+ ds_put_format(&s, ": facet associated with wrong rule (was "
+ "table=%"PRIu8",", facet->rule->up.table_id);
+ cls_rule_format(&facet->rule->up.cr, &s);
+ ds_put_format(&s, ") (should have been table=%"PRIu8",",
+ rule->up.table_id);
+ cls_rule_format(&rule->up.cr, &s);
+ ds_put_char(&s, ')');
+
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+ } else {
+ ok = true;
+ }
+
+ /* Check the datapath actions for consistency. */
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ struct action_xlate_ctx ctx;
+ struct ofpbuf *odp_actions;
+ bool actions_changed;
+ bool should_install;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ subfacet->initial_tci, rule, 0, NULL);
+ odp_actions = xlate_actions(&ctx, rule->up.actions,
+ rule->up.n_actions);
+
+ should_install = (ctx.may_set_up_flow
+ && subfacet->key_fitness != ODP_FIT_TOO_LITTLE);
+ if (!should_install && !subfacet->installed) {
+ /* The actions for uninstallable flows may vary from one packet to
+ * the next, so don't compare the actions. */
+ goto next;
+ }
+
+ actions_changed = (subfacet->actions_len != odp_actions->size
+ || memcmp(subfacet->actions, odp_actions->data,
+ subfacet->actions_len));
+ if (should_install != subfacet->installed || actions_changed) {
+ if (ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ ok = false;
+ }
+
+ if (may_log) {
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
+ struct ds s;
+
+ ds_init(&s);
+ subfacet_get_key(subfacet, &keybuf, &key);
+ odp_flow_key_format(key.data, key.size, &s);
+
+ ds_put_cstr(&s, ": inconsistency in subfacet");
+ if (should_install != subfacet->installed) {
+ enum odp_key_fitness fitness = subfacet->key_fitness;
+
+ ds_put_format(&s, " (should%s have been installed)",
+ should_install ? "" : " not");
+ ds_put_format(&s, " (may_set_up_flow=%s, fitness=%s)",
+ ctx.may_set_up_flow ? "true" : "false",
+ odp_key_fitness_to_string(fitness));
+ }
+ if (actions_changed) {
+ ds_put_cstr(&s, " (actions were: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_cstr(&s, ") (correct actions: ");
+ format_odp_actions(&s, odp_actions->data,
+ odp_actions->size);
+ ds_put_char(&s, ')');
+ } else {
+ ds_put_cstr(&s, " (actions: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_char(&s, ')');
+ }
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+ }
+
+ next:
+ ofpbuf_delete(odp_actions);
+ }
+
+ return ok;
+}
+
+/* Re-searches the classifier for 'facet':