+ if (odp_actions.size) {
+ struct dpif_execute *execute = &op->dpif_op.u.execute;
+
+ init_flow_miss_execute_op(miss, packet, op);
+ execute->actions = odp_actions.data;
+ execute->actions_len = odp_actions.size;
+ op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
+
+ (*n_ops)++;
+ } else {
+ ofpbuf_uninit(&odp_actions);
+ }
+ }
+}
+
+/* Handles 'miss', which matches 'facet'. May add any required datapath
+ * operations to 'ops', incrementing '*n_ops' for each new op. */
+static void
+handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
+ struct flow_miss_op *ops, size_t *n_ops)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ enum subfacet_path want_path;
+ struct subfacet *subfacet;
+ struct ofpbuf *packet;
+
+ subfacet = subfacet_create(facet,
+ miss->key_fitness, miss->key, miss->key_len,
+ miss->initial_tci);
+
+ LIST_FOR_EACH (packet, list_node, &miss->packets) {
+ struct flow_miss_op *op = &ops[*n_ops];
+ struct dpif_flow_stats stats;
+ struct ofpbuf odp_actions;
+
+ handle_flow_miss_common(facet->rule, packet, &miss->flow);
+
+ ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
+ if (!subfacet->actions || subfacet->slow) {
+ subfacet_make_actions(subfacet, packet, &odp_actions);
+ }
+
+ dpif_flow_stats_extract(&facet->flow, packet, &stats);
+ subfacet_update_stats(subfacet, &stats);
+
+ if (subfacet->actions_len) {
+ struct dpif_execute *execute = &op->dpif_op.u.execute;
+
+ init_flow_miss_execute_op(miss, packet, op);
+ op->subfacet = subfacet;
+ if (!subfacet->slow) {
+ execute->actions = subfacet->actions;
+ execute->actions_len = subfacet->actions_len;
+ ofpbuf_uninit(&odp_actions);
+ } else {
+ execute->actions = odp_actions.data;
+ execute->actions_len = odp_actions.size;
+ op->garbage = ofpbuf_get_uninit_pointer(&odp_actions);
+ }
+
+ (*n_ops)++;
+ } else {
+ ofpbuf_uninit(&odp_actions);
+ }
+ }
+
+ want_path = subfacet_want_path(subfacet->slow);
+ if (miss->upcall_type == DPIF_UC_MISS || subfacet->path != want_path) {
+ struct flow_miss_op *op = &ops[(*n_ops)++];
+ struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
+
+ op->subfacet = subfacet;
+ op->garbage = NULL;
+ op->dpif_op.type = DPIF_OP_FLOW_PUT;
+ put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+ put->key = miss->key;
+ put->key_len = miss->key_len;
+ if (want_path == SF_FAST_PATH) {
+ put->actions = subfacet->actions;
+ put->actions_len = subfacet->actions_len;
+ } else {
+ compose_slow_path(ofproto, &facet->flow, subfacet->slow,
+ op->stub, sizeof op->stub,
+ &put->actions, &put->actions_len);
+ }
+ put->stats = NULL;
+ }
+}
+
+/* Handles flow miss 'miss' on 'ofproto'. May add any required datapath
+ * operations to 'ops', incrementing '*n_ops' for each new op. */
+static void
+handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
+ struct flow_miss_op *ops, size_t *n_ops)
+{
+ struct facet *facet;
+ uint32_t hash;
+
+ /* The caller must ensure that miss->hmap_node.hash contains
+ * flow_hash(miss->flow, 0). */
+ hash = miss->hmap_node.hash;
+
+ facet = facet_lookup_valid(ofproto, &miss->flow, hash);
+ if (!facet) {
+ struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow);
+
+ if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
+ handle_flow_miss_without_facet(miss, rule, ops, n_ops);
+ return;
+ }
+
+ facet = facet_create(rule, &miss->flow, hash);
+ }
+ handle_flow_miss_with_facet(miss, facet, ops, n_ops);
+}
+
+/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
+ * OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns
+ * an ODP_FIT_* value that indicates how well 'key' fits our expectations for
+ * what a flow key should contain.
+ *
+ * This function also includes some logic to help make VLAN splinters
+ * transparent to the rest of the upcall processing logic. In particular, if
+ * the extracted in_port is a VLAN splinter port, it replaces flow->in_port by
+ * the "real" port, sets flow->vlan_tci correctly for the VLAN of the VLAN
+ * splinter port, and pushes a VLAN header onto 'packet' (if it is nonnull).
+ *
+ * Sets '*initial_tci' to the VLAN TCI with which the packet was really
+ * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow().
+ * (This differs from the value returned in flow->vlan_tci only for packets
+ * received on VLAN splinters.)
+ */