+facet_reset_counters(struct facet *facet)
+{
+ facet->packet_count = 0;
+ facet->byte_count = 0;
+ facet->prev_packet_count = 0;
+ facet->prev_byte_count = 0;
+ facet->accounted_bytes = 0;
+}
+
+static void
+facet_push_stats(struct facet *facet)
+{
+ struct dpif_flow_stats stats;
+
+ assert(facet->packet_count >= facet->prev_packet_count);
+ assert(facet->byte_count >= facet->prev_byte_count);
+ assert(facet->used >= facet->prev_used);
+
+ stats.n_packets = facet->packet_count - facet->prev_packet_count;
+ stats.n_bytes = facet->byte_count - facet->prev_byte_count;
+ stats.used = facet->used;
+ stats.tcp_flags = 0;
+
+ if (stats.n_packets || stats.n_bytes || facet->used > facet->prev_used) {
+ facet->prev_packet_count = facet->packet_count;
+ facet->prev_byte_count = facet->byte_count;
+ facet->prev_used = facet->used;
+
+ flow_push_stats(facet->rule, &facet->flow, &stats);
+
+ update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
+ facet->mirrors, stats.n_packets, stats.n_bytes);
+ }
+}
+
+static void
+rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
+{
+ rule->packet_count += stats->n_packets;
+ rule->byte_count += stats->n_bytes;
+ ofproto_rule_update_used(&rule->up, stats->used);
+}
+
+/* Pushes flow statistics to the rules which 'flow' resubmits into given
+ * 'rule''s actions and mirrors. */
+static void
+flow_push_stats(struct rule_dpif *rule,
+ const struct flow *flow, const struct dpif_flow_stats *stats)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct action_xlate_ctx ctx;
+
+ ofproto_rule_update_used(&rule->up, stats->used);
+
+ action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
+ 0, NULL);
+ ctx.resubmit_stats = stats;
+ xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
+}
+\f
+/* Subfacets. */
+
+static struct subfacet *
+subfacet_find__(struct ofproto_dpif *ofproto,
+ const struct nlattr *key, size_t key_len, uint32_t key_hash,
+ const struct flow *flow)
+{
+ struct subfacet *subfacet;
+
+ HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
+ &ofproto->subfacets) {
+ if (subfacet->key
+ ? (subfacet->key_len == key_len
+ && !memcmp(key, subfacet->key, key_len))
+ : flow_equal(flow, &subfacet->facet->flow)) {
+ return subfacet;
+ }
+ }
+
+ return NULL;
+}
+
+/* Searches 'facet' (within 'ofproto') for a subfacet with the specified
+ * 'key_fitness', 'key', and 'key_len'. Returns the existing subfacet if
+ * there is one, otherwise creates and returns a new subfacet.
+ *
+ * If the returned subfacet is new, then subfacet->actions will be NULL, in
+ * which case the caller must populate the actions with
+ * subfacet_make_actions(). */
+static struct subfacet *
+subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
+ const struct nlattr *key, size_t key_len, ovs_be16 initial_tci)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ uint32_t key_hash = odp_flow_key_hash(key, key_len);
+ struct subfacet *subfacet;
+
+ subfacet = subfacet_find__(ofproto, key, key_len, key_hash, &facet->flow);
+ if (subfacet) {
+ if (subfacet->facet == facet) {
+ return subfacet;
+ }
+
+ /* This shouldn't happen. */
+ VLOG_ERR_RL(&rl, "subfacet with wrong facet");
+ subfacet_destroy(subfacet);
+ }
+
+ subfacet = (list_is_empty(&facet->subfacets)
+ ? &facet->one_subfacet
+ : xmalloc(sizeof *subfacet));
+ hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
+ list_push_back(&facet->subfacets, &subfacet->list_node);
+ subfacet->facet = facet;
+ subfacet->key_fitness = key_fitness;
+ if (key_fitness != ODP_FIT_PERFECT) {
+ subfacet->key = xmemdup(key, key_len);
+ subfacet->key_len = key_len;
+ } else {
+ subfacet->key = NULL;
+ subfacet->key_len = 0;
+ }
+ subfacet->used = time_msec();
+ subfacet->dp_packet_count = 0;
+ subfacet->dp_byte_count = 0;
+ subfacet->actions_len = 0;
+ subfacet->actions = NULL;
+ subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
+ ? SLOW_MATCH
+ : 0);
+ subfacet->path = SF_NOT_INSTALLED;
+ subfacet->initial_tci = initial_tci;
+
+ return subfacet;
+}
+
+/* Searches 'ofproto' for a subfacet with the given 'key', 'key_len', and
+ * 'flow'. Returns the subfacet if one exists, otherwise NULL. */
+static struct subfacet *
+subfacet_find(struct ofproto_dpif *ofproto,
+ const struct nlattr *key, size_t key_len)
+{
+ uint32_t key_hash = odp_flow_key_hash(key, key_len);
+ enum odp_key_fitness fitness;
+ struct flow flow;
+
+ fitness = odp_flow_key_to_flow(key, key_len, &flow);
+ if (fitness == ODP_FIT_ERROR) {
+ return NULL;
+ }
+
+ return subfacet_find__(ofproto, key, key_len, key_hash, &flow);
+}
+
+/* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from
+ * its facet within 'ofproto', and frees it. */
+static void
+subfacet_destroy__(struct subfacet *subfacet)
+{
+ struct facet *facet = subfacet->facet;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+
+ subfacet_uninstall(subfacet);
+ hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
+ list_remove(&subfacet->list_node);
+ free(subfacet->key);
+ free(subfacet->actions);
+ if (subfacet != &facet->one_subfacet) {
+ free(subfacet);
+ }
+}
+
+/* Destroys 'subfacet', as with subfacet_destroy__(), and then if this was the
+ * last remaining subfacet in its facet destroys the facet too. */
+static void
+subfacet_destroy(struct subfacet *subfacet)
+{
+ struct facet *facet = subfacet->facet;
+
+ if (list_is_singleton(&facet->subfacets)) {
+ /* facet_remove() needs at least one subfacet (it will remove it). */
+ facet_remove(facet);
+ } else {
+ subfacet_destroy__(subfacet);
+ }
+}
+
+/* Initializes 'key' with the sequence of OVS_KEY_ATTR_* Netlink attributes
+ * that can be used to refer to 'subfacet'. The caller must provide 'keybuf'
+ * for use as temporary storage. */
+static void
+subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf,
+ struct ofpbuf *key)
+{
+ if (!subfacet->key) {
+ ofpbuf_use_stack(key, keybuf, sizeof *keybuf);
+ odp_flow_key_from_flow(key, &subfacet->facet->flow);
+ } else {
+ ofpbuf_use_const(key, subfacet->key, subfacet->key_len);
+ }
+}
+
+/* Composes the datapath actions for 'subfacet' based on its rule's actions.
+ * Translates the actions into 'odp_actions', which the caller must have
+ * initialized and is responsible for uninitializing. */
+static void
+subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
+ struct ofpbuf *odp_actions)
+{
+ struct facet *facet = subfacet->facet;
+ struct rule_dpif *rule = facet->rule;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+ struct action_xlate_ctx ctx;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
+ rule, 0, packet);
+ xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
+ facet->tags = ctx.tags;
+ facet->has_learn = ctx.has_learn;
+ facet->has_normal = ctx.has_normal;
+ facet->has_fin_timeout = ctx.has_fin_timeout;
+ facet->nf_flow.output_iface = ctx.nf_output_iface;
+ facet->mirrors = ctx.mirrors;
+
+ subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+ if (subfacet->actions_len != odp_actions->size
+ || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
+ free(subfacet->actions);
+ subfacet->actions_len = odp_actions->size;
+ subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
+ }
+}
+
+/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
+ * bytes of actions in 'actions'. If 'stats' is non-null, statistics counters
+ * in the datapath will be zeroed and 'stats' will be updated with traffic new
+ * since 'subfacet' was last updated.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+subfacet_install(struct subfacet *subfacet,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *stats,
+ enum slow_path_reason slow)
+{
+ struct facet *facet = subfacet->facet;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ enum subfacet_path path = subfacet_want_path(slow);
+ uint64_t slow_path_stub[128 / 8];
+ struct odputil_keybuf keybuf;
+ enum dpif_flow_put_flags flags;
+ struct ofpbuf key;
+ int ret;
+
+ flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+ if (stats) {
+ flags |= DPIF_FP_ZERO_STATS;
+ }
+
+ if (path == SF_SLOW_PATH) {
+ compose_slow_path(ofproto, &facet->flow, slow,
+ slow_path_stub, sizeof slow_path_stub,
+ &actions, &actions_len);
+ }
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
+ actions, actions_len, stats);
+
+ if (stats) {
+ subfacet_reset_dp_stats(subfacet, stats);
+ }
+
+ if (!ret) {
+ subfacet->path = path;
+ }
+ return ret;
+}
+
+static int
+subfacet_reinstall(struct subfacet *subfacet, struct dpif_flow_stats *stats)
+{
+ return subfacet_install(subfacet, subfacet->actions, subfacet->actions_len,
+ stats, subfacet->slow);
+}
+
+/* If 'subfacet' is installed in the datapath, uninstalls it. */
+static void
+subfacet_uninstall(struct subfacet *subfacet)
+{
+ if (subfacet->path != SF_NOT_INSTALLED) {
+ struct rule_dpif *rule = subfacet->facet->rule;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct odputil_keybuf keybuf;
+ struct dpif_flow_stats stats;
+ struct ofpbuf key;
+ int error;
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ error = dpif_flow_del(ofproto->dpif, key.data, key.size, &stats);
+ subfacet_reset_dp_stats(subfacet, &stats);
+ if (!error) {
+ subfacet_update_stats(subfacet, &stats);
+ }
+ subfacet->path = SF_NOT_INSTALLED;
+ } else {
+ assert(subfacet->dp_packet_count == 0);
+ assert(subfacet->dp_byte_count == 0);
+ }
+}
+
+/* Resets 'subfacet''s datapath statistics counters. This should be called
+ * when 'subfacet''s statistics are cleared in the datapath. If 'stats' is
+ * non-null, it should contain the statistics returned by dpif when 'subfacet'
+ * was reset in the datapath. 'stats' will be modified to include only
+ * statistics new since 'subfacet' was last updated. */
+static void
+subfacet_reset_dp_stats(struct subfacet *subfacet,
+ struct dpif_flow_stats *stats)