+static const char *
+subfacet_path_to_string(enum subfacet_path path)
+{
+ switch (path) {
+ case SF_NOT_INSTALLED:
+ return "not installed";
+ case SF_FAST_PATH:
+ return "in fast path";
+ case SF_SLOW_PATH:
+ return "in slow path";
+ default:
+ return "<error>";
+ }
+}
+
+/* Returns the path in which a subfacet should be installed if its 'slow'
+ * member has the specified value. */
+static enum subfacet_path
+subfacet_want_path(enum slow_path_reason slow)
+{
+ return slow ? SF_SLOW_PATH : SF_FAST_PATH;
+}
+
+/* Returns true if 'subfacet' needs to have its datapath flow updated,
+ * supposing that its actions have been recalculated as 'want_actions' and that
+ * 'slow' is nonzero iff 'subfacet' should be in the slow path. */
+static bool
+subfacet_should_install(struct subfacet *subfacet, enum slow_path_reason slow,
+ const struct ofpbuf *want_actions)
+{
+ enum subfacet_path want_path = subfacet_want_path(slow);
+ return (want_path != subfacet->path
+ || (want_path == SF_FAST_PATH
+ && (subfacet->actions_len != want_actions->size
+ || memcmp(subfacet->actions, want_actions->data,
+ subfacet->actions_len))));
+}
+
+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);
+
+ uint64_t odp_actions_stub[1024 / 8];
+ struct ofpbuf odp_actions;
+
+ 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);
+ ok = rule == facet->rule;
+ if (!ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ 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);
+ }
+ }
+
+ /* Check the datapath actions for consistency. */
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ enum subfacet_path want_path;
+ struct odputil_keybuf keybuf;
+ struct action_xlate_ctx ctx;
+ struct ofpbuf key;
+ struct ds s;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ subfacet->initial_tci, rule, 0, NULL);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
+ &odp_actions);
+
+ if (subfacet->path == SF_NOT_INSTALLED) {
+ /* This only happens if the datapath reported an error when we
+ * tried to install the flow. Don't flag another error here. */
+ continue;
+ }
+
+ want_path = subfacet_want_path(subfacet->slow);
+ if (want_path == SF_SLOW_PATH && subfacet->path == SF_SLOW_PATH) {
+ /* The actions for slow-path flows may legitimately vary from one
+ * packet to the next. We're done. */
+ continue;
+ }
+
+ if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
+ continue;
+ }
+
+ /* Inconsistency! */
+ if (ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ ok = false;
+ }
+ if (!may_log) {
+ /* Rate-limited, skip reporting. */
+ continue;
+ }
+
+ 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 (want_path != subfacet->path) {
+ enum odp_key_fitness fitness = subfacet->key_fitness;
+
+ ds_put_format(&s, " (%s, fitness=%s)",
+ subfacet_path_to_string(subfacet->path),
+ odp_key_fitness_to_string(fitness));
+ ds_put_format(&s, " (should have been %s)",
+ subfacet_path_to_string(want_path));
+ } else if (want_path == SF_FAST_PATH) {
+ 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);
+ }
+ ofpbuf_uninit(&odp_actions);
+
+ return ok;
+}
+
+/* Re-searches the classifier for 'facet':