+static struct udpif_key *
+ukey_lookup(struct revalidator *revalidator, struct udpif_flow_dump *udump)
+{
+ struct udpif_key *ukey;
+
+ HMAP_FOR_EACH_WITH_HASH (ukey, hmap_node, udump->key_hash,
+ &revalidator->ukeys) {
+ if (ukey->key_len == udump->key_len
+ && !memcmp(ukey->key, udump->key, udump->key_len)) {
+ return ukey;
+ }
+ }
+ return NULL;
+}
+
+static struct udpif_key *
+ukey_create(const struct nlattr *key, size_t key_len, long long int used)
+{
+ struct udpif_key *ukey = xmalloc(sizeof *ukey);
+
+ ukey->key = (struct nlattr *) &ukey->key_buf;
+ memcpy(&ukey->key_buf, key, key_len);
+ ukey->key_len = key_len;
+
+ ukey->mark = false;
+ ukey->flow_exists = true;
+ ukey->created = used ? used : time_msec();
+ memset(&ukey->stats, 0, sizeof ukey->stats);
+ ukey->xcache = NULL;
+
+ return ukey;
+}
+
+static void
+ukey_delete(struct revalidator *revalidator, struct udpif_key *ukey)
+{
+ hmap_remove(&revalidator->ukeys, &ukey->hmap_node);
+ xlate_cache_delete(ukey->xcache);
+ free(ukey);
+}
+
+static bool
+should_revalidate(uint64_t packets, long long int used)
+{
+ long long int metric, now, duration;
+
+ /* Calculate the mean time between seeing these packets. If this
+ * exceeds the threshold, then delete the flow rather than performing
+ * costly revalidation for flows that aren't being hit frequently.
+ *
+ * This is targeted at situations where the dump_duration is high (~1s),
+ * and revalidation is triggered by a call to udpif_revalidate(). In
+ * these situations, revalidation of all flows causes fluctuations in the
+ * flow_limit due to the interaction with the dump_duration and max_idle.
+ * This tends to result in deletion of low-throughput flows anyway, so
+ * skip the revalidation and just delete those flows. */
+ packets = MAX(packets, 1);
+ now = MAX(used, time_msec());
+ duration = now - used;
+ metric = duration / packets;
+
+ if (metric > 200) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+revalidate_ukey(struct udpif *udpif, struct udpif_flow_dump *udump,
+ struct udpif_key *ukey)
+{
+ struct ofpbuf xout_actions, *actions;
+ uint64_t slow_path_buf[128 / 8];
+ struct xlate_out xout, *xoutp;
+ struct netflow *netflow;
+ struct flow flow, udump_mask;
+ struct ofproto_dpif *ofproto;
+ struct dpif_flow_stats push;
+ uint32_t *udump32, *xout32;
+ odp_port_t odp_in_port;
+ struct xlate_in xin;
+ long long int last_used;
+ int error;
+ size_t i;
+ bool may_learn, ok;
+
+ ok = false;
+ xoutp = NULL;
+ actions = NULL;
+ netflow = NULL;
+
+ /* If we don't need to revalidate, we can simply push the stats contained
+ * in the udump, otherwise we'll have to get the actions so we can check
+ * them. */
+ if (udump->need_revalidate) {
+ if (dpif_flow_get(udpif->dpif, ukey->key, ukey->key_len, &actions,
+ &udump->stats)) {
+ goto exit;
+ }
+ }
+
+ last_used = ukey->stats.used;
+ push.used = udump->stats.used;
+ push.tcp_flags = udump->stats.tcp_flags;
+ push.n_packets = udump->stats.n_packets > ukey->stats.n_packets
+ ? udump->stats.n_packets - ukey->stats.n_packets
+ : 0;
+ push.n_bytes = udump->stats.n_bytes > ukey->stats.n_bytes
+ ? udump->stats.n_bytes - ukey->stats.n_bytes
+ : 0;
+ ukey->stats = udump->stats;
+
+ if (udump->need_revalidate && last_used
+ && !should_revalidate(push.n_packets, last_used)) {
+ ok = false;
+ goto exit;
+ }
+
+ if (!push.n_packets && !udump->need_revalidate) {
+ ok = true;
+ goto exit;
+ }
+
+ may_learn = push.n_packets > 0;
+ if (ukey->xcache && !udump->need_revalidate) {
+ xlate_push_stats(ukey->xcache, may_learn, &push);
+ ok = true;
+ goto exit;
+ }
+
+ error = xlate_receive(udpif->backer, NULL, ukey->key, ukey->key_len, &flow,
+ &ofproto, NULL, NULL, &netflow, &odp_in_port);
+ if (error) {
+ goto exit;
+ }
+
+ if (udump->need_revalidate) {
+ xlate_cache_clear(ukey->xcache);
+ }
+ if (!ukey->xcache) {
+ ukey->xcache = xlate_cache_new();
+ }
+
+ xlate_in_init(&xin, ofproto, &flow, NULL, push.tcp_flags, NULL);
+ xin.resubmit_stats = push.n_packets ? &push : NULL;
+ xin.xcache = ukey->xcache;
+ xin.may_learn = may_learn;
+ xin.skip_wildcards = !udump->need_revalidate;
+ xlate_actions(&xin, &xout);
+ xoutp = &xout;
+
+ if (!udump->need_revalidate) {
+ ok = true;
+ goto exit;
+ }
+
+ if (!xout.slow) {
+ ofpbuf_use_const(&xout_actions, ofpbuf_data(&xout.odp_actions),
+ ofpbuf_size(&xout.odp_actions));