- memset(&miss->stats, 0, sizeof miss->stats);
- miss->stats.used = time_msec();
- LIST_FOR_EACH (packet, list_node, &miss->packets) {
- miss->stats.tcp_flags |= packet_get_tcp_flags(packet, &miss->flow);
- miss->stats.n_bytes += packet->size;
- miss->stats.n_packets++;
- }
-
- flow_wildcards_init_catchall(&wc);
- rule_dpif_lookup(ofproto, &miss->flow, &wc, &rule);
- rule_dpif_credit_stats(rule, &miss->stats);
- xlate_in_init(&xin, ofproto, &miss->flow, rule, miss->stats.tcp_flags,
- NULL);
- xin.may_learn = true;
- xin.resubmit_stats = &miss->stats;
- xlate_actions(&xin, &miss->xout);
- flow_wildcards_or(&miss->xout.wc, &miss->xout.wc, &wc);
-
- if (rule_dpif_fail_open(rule)) {
- LIST_FOR_EACH (packet, list_node, &miss->packets) {
- struct ofputil_packet_in *pin;
-
- /* 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. */
- pin = xmalloc(sizeof(*pin));
- pin->packet = xmemdup(packet->data, packet->size);
- pin->packet_len = packet->size;
- pin->reason = OFPR_NO_MATCH;
- pin->controller_id = 0;
- pin->table_id = 0;
- pin->cookie = 0;
- pin->send_len = 0; /* Not used for flow table misses. */
- flow_get_metadata(&miss->flow, &pin->fmd);
- ofproto_dpif_send_packet_in(ofproto, pin);
+ 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, &miss->key_fitness,
+ &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;
+
+ flow_extract(packet, flow.skb_priority, flow.pkt_mark,
+ &flow.tunnel, &flow.in_port, &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 |= packet_get_tcp_flags(packet, &miss->flow);
+ miss->stats.n_bytes += packet->size;
+ 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);
+ }
+
+ /* Initialize each 'struct flow_miss's ->xout.
+ *
+ * We do this per-flow_miss rather than per-packet because, most commonly,
+ * all the packets in a flow can use the same translation.
+ *
+ * We can't do this in the previous loop because we need the TCP flags for
+ * all the packets in each miss. */
+ fail_open = false;
+ HMAP_FOR_EACH (miss, hmap_node, &misses) {
+ struct xlate_in xin;
+
+ xlate_in_init(&xin, miss->ofproto, &miss->flow, NULL,
+ miss->stats.tcp_flags, NULL);
+ xin.may_learn = true;
+
+ if (miss->upcall_type == DPIF_UC_MISS) {
+ xin.resubmit_stats = &miss->stats;
+ } else {
+ /* For non-miss upcalls, there's a flow in the datapath which this
+ * packet was accounted to. Presumably the revalidators will deal
+ * with pushing its stats eventually. */