+ struct flow_miss miss_buf[FLOW_MISS_MAX_BATCH];
+ struct dpif_op *opsp[FLOW_MISS_MAX_BATCH * 2];
+ struct dpif_op ops[FLOW_MISS_MAX_BATCH * 2];
+ struct flow_miss *miss, *next_miss;
+ struct upcall *upcall, *next;
+ size_t n_misses, n_ops, i;
+ unsigned int flow_limit;
+ bool fail_open, may_put;
+ enum upcall_type type;
+
+ atomic_read(&udpif->flow_limit, &flow_limit);
+ may_put = udpif_get_n_flows(udpif) < flow_limit;
+
+ /* Extract the flow from each upcall. Construct in 'misses' a hash table
+ * that maps each unique flow to a 'struct flow_miss'.
+ *
+ * Most commonly there is a single packet per flow_miss, but there are
+ * several reasons why there might be more than one, e.g.:
+ *
+ * - The dpif packet interface does not support TSO (or UFO, etc.), so a
+ * large packet sent to userspace is split into a sequence of smaller
+ * ones.
+ *
+ * - A stream of quickly arriving packets in an established "slow-pathed"
+ * flow.
+ *
+ * - Rarely, a stream of quickly arriving packets in a flow not yet
+ * established. (This is rare because most protocols do not send
+ * multiple back-to-back packets before receiving a reply from the
+ * other end of the connection, which gives OVS a chance to set up a
+ * datapath flow.)
+ */
+ n_misses = 0;
+ LIST_FOR_EACH_SAFE (upcall, next, list_node, upcalls) {
+ struct dpif_upcall *dupcall = &upcall->dpif_upcall;
+ struct flow_miss *miss = &miss_buf[n_misses];
+ struct ofpbuf *packet = &dupcall->packet;
+ struct flow_miss *existing_miss;
+ struct ofproto_dpif *ofproto;
+ struct dpif_sflow *sflow;
+ struct dpif_ipfix *ipfix;
+ odp_port_t odp_in_port;
+ struct flow flow;
+ int error;
+
+ error = xlate_receive(udpif->backer, packet, dupcall->key,
+ dupcall->key_len, &flow,
+ &ofproto, &ipfix, &sflow, NULL, &odp_in_port);
+ if (error) {
+ if (error == ENODEV) {
+ /* Received packet on datapath port for which we couldn't
+ * associate an ofproto. This can happen if a port is removed
+ * while traffic is being received. Print a rate-limited
+ * message in case it happens frequently. Install a drop flow
+ * so that future packets of the flow are inexpensively dropped
+ * in the kernel. */
+ VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
+ "port %"PRIu32, odp_in_port);
+ dpif_flow_put(udpif->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+ dupcall->key, dupcall->key_len, NULL, 0, NULL, 0,
+ NULL);
+ }
+ list_remove(&upcall->list_node);
+ upcall_destroy(upcall);
+ continue;
+ }
+
+ type = classify_upcall(upcall);
+ if (type == MISS_UPCALL) {
+ uint32_t hash;
+ struct pkt_metadata md = pkt_metadata_from_flow(&flow);
+
+ flow_extract(packet, &md, &miss->flow);
+ hash = flow_hash(&miss->flow, 0);
+ existing_miss = flow_miss_find(&misses, ofproto, &miss->flow,
+ hash);
+ if (!existing_miss) {
+ hmap_insert(&misses, &miss->hmap_node, hash);
+ miss->ofproto = ofproto;
+ miss->key = dupcall->key;
+ miss->key_len = dupcall->key_len;
+ miss->upcall_type = dupcall->type;
+ miss->stats.n_packets = 0;
+ miss->stats.n_bytes = 0;
+ miss->stats.used = time_msec();
+ miss->stats.tcp_flags = 0;
+ miss->odp_in_port = odp_in_port;
+ miss->put = false;
+
+ n_misses++;
+ } else {
+ miss = existing_miss;
+ }
+ miss->stats.tcp_flags |= ntohs(miss->flow.tcp_flags);
+ miss->stats.n_bytes += ofpbuf_size(packet);
+ miss->stats.n_packets++;
+
+ upcall->flow_miss = miss;
+ continue;
+ }
+
+ switch (type) {
+ case SFLOW_UPCALL:
+ if (sflow) {
+ union user_action_cookie cookie;
+
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(dupcall->userdata),
+ sizeof cookie.sflow);
+ dpif_sflow_received(sflow, packet, &flow, odp_in_port,
+ &cookie);
+ }
+ break;
+ case IPFIX_UPCALL:
+ if (ipfix) {
+ dpif_ipfix_bridge_sample(ipfix, packet, &flow);
+ }
+ break;
+ case FLOW_SAMPLE_UPCALL:
+ if (ipfix) {
+ union user_action_cookie cookie;
+
+ memset(&cookie, 0, sizeof cookie);
+ memcpy(&cookie, nl_attr_get(dupcall->userdata),
+ sizeof cookie.flow_sample);
+
+ /* The flow reflects exactly the contents of the packet.
+ * Sample the packet using it. */
+ dpif_ipfix_flow_sample(ipfix, packet, &flow,
+ cookie.flow_sample.collector_set_id,
+ cookie.flow_sample.probability,
+ cookie.flow_sample.obs_domain_id,
+ cookie.flow_sample.obs_point_id);
+ }
+ break;
+ case BAD_UPCALL:
+ break;
+ case MISS_UPCALL:
+ OVS_NOT_REACHED();
+ }
+
+ dpif_ipfix_unref(ipfix);
+ dpif_sflow_unref(sflow);
+
+ list_remove(&upcall->list_node);
+ upcall_destroy(upcall);