+ LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) {
+ list_remove(&packet->list_node);
+ ofproto->n_matches++;
+
+ if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) {
+ /*
+ * Extra-special case for fail-open mode.
+ *
+ * We are in fail-open mode and the packet matched the fail-open
+ * rule, but we are connected to a controller too. We should send
+ * the packet up to the controller in the hope that it will try to
+ * set up a flow and thereby allow us to exit fail-open.
+ *
+ * See the top-level comment in fail-open.c for more information.
+ */
+ send_packet_in_miss(ofproto, packet, flow, true);
+ }
+
+ if (!facet->may_install) {
+ facet_make_actions(ofproto, facet, packet);
+ }
+ if (!execute_controller_action(ofproto, &facet->flow,
+ facet->actions, facet->actions_len,
+ packet)) {
+ struct flow_miss_op *op = &ops[(*n_ops)++];
+ struct dpif_execute *execute = &op->dpif_op.execute;
+
+ op->facet = facet;
+ execute->type = DPIF_OP_EXECUTE;
+ execute->key = miss->key;
+ execute->key_len = miss->key_len;
+ execute->actions
+ = (facet->may_install
+ ? facet->actions
+ : xmemdup(facet->actions, facet->actions_len));
+ execute->actions_len = facet->actions_len;
+ execute->packet = packet;
+ }
+ }
+
+ if (facet->may_install) {
+ struct flow_miss_op *op = &ops[(*n_ops)++];
+ struct dpif_flow_put *put = &op->dpif_op.flow_put;
+
+ op->facet = facet;
+ put->type = DPIF_OP_FLOW_PUT;
+ put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+ put->key = miss->key;
+ put->key_len = miss->key_len;
+ put->actions = facet->actions;
+ put->actions_len = facet->actions_len;
+ put->stats = NULL;
+ }
+}
+
+static void
+handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
+ size_t n_upcalls)
+{
+ struct dpif_upcall *upcall;
+ struct flow_miss *miss, *next_miss;
+ struct flow_miss_op flow_miss_ops[FLOW_MISS_MAX_BATCH * 2];
+ union dpif_op *dpif_ops[FLOW_MISS_MAX_BATCH * 2];
+ struct hmap todo;
+ size_t n_ops;
+ size_t i;
+
+ if (!n_upcalls) {
+ return;
+ }
+
+ /* Construct the to-do list.
+ *
+ * This just amounts to extracting the flow from each packet and sticking
+ * the packets that have the same flow in the same "flow_miss" structure so
+ * that we can process them together. */
+ hmap_init(&todo);
+ for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) {
+ struct flow_miss *miss;
+ struct flow flow;
+
+ /* Obtain in_port and tun_id, at least, then set 'flow''s header
+ * pointers. */
+ odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+ flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow);
+
+ /* Handle 802.1ag and LACP specially. */
+ if (process_special(ofproto, &flow, upcall->packet)) {
+ ofpbuf_delete(upcall->packet);
+ ofproto->n_matches++;
+ continue;
+ }
+
+ /* Add other packets to a to-do list. */
+ miss = flow_miss_create(&todo, &flow, upcall->key, upcall->key_len);
+ list_push_back(&miss->packets, &upcall->packet->list_node);
+ }
+
+ /* Process each element in the to-do list, constructing the set of
+ * operations to batch. */
+ n_ops = 0;
+ HMAP_FOR_EACH_SAFE (miss, next_miss, hmap_node, &todo) {
+ handle_flow_miss(ofproto, miss, flow_miss_ops, &n_ops);
+ ofpbuf_list_delete(&miss->packets);
+ hmap_remove(&todo, &miss->hmap_node);
+ free(miss);
+ }
+ assert(n_ops <= ARRAY_SIZE(flow_miss_ops));
+ hmap_destroy(&todo);
+
+ /* Execute batch. */
+ for (i = 0; i < n_ops; i++) {
+ dpif_ops[i] = &flow_miss_ops[i].dpif_op;