+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ static const struct ovs_key_ipv4_tunnel tun_key;
+ struct tnl_cache *cache;
+ void *cache_data;
+ int cache_len;
+ struct hh_cache *hh;
+ int tunnel_hlen;
+
+ if (!(mutable->flags & TNL_F_HDR_CACHE))
+ return NULL;
+
+ tunnel_hlen = tnl_vport->tnl_ops->hdr_len(mutable, &tun_key);
+ if (tunnel_hlen < 0)
+ return NULL;
+
+ tunnel_hlen += sizeof(struct iphdr);
+
+ /*
+ * If there is no entry in the ARP cache or if this device does not
+ * support hard header caching just fall back to the IP stack.
+ */
+
+ hh = rt_hh(rt);
+ if (!hh)
+ return NULL;
+
+ /*
+ * If lock is contended fall back to directly building the header.
+ * We're not going to help performance by sitting here spinning.
+ */
+ if (!spin_trylock(&tnl_vport->cache_lock))
+ return NULL;
+
+ cache = cache_dereference(tnl_vport);
+ if (check_cache_valid(cache, mutable))
+ goto unlock;
+ else
+ cache = NULL;
+
+ cache_len = LL_RESERVED_SPACE(rt_dst(rt).dev) + tunnel_hlen;
+
+ cache = kzalloc(ALIGN(sizeof(struct tnl_cache), CACHE_DATA_ALIGN) +
+ cache_len, GFP_ATOMIC);
+ if (!cache)
+ goto unlock;
+
+ create_eth_hdr(cache, hh);
+ cache_data = get_cached_header(cache) + cache->hh_len;
+ cache->len = cache->hh_len + tunnel_hlen;
+
+ create_tunnel_header(vport, mutable, &tun_key, rt, cache_data);
+
+ cache->mutable_seq = mutable->seq;
+ cache->rt = rt;
+#ifdef NEED_CACHE_TIMEOUT
+ cache->expiration = jiffies + tnl_vport->cache_exp_interval;
+#endif
+
+ if (ovs_is_internal_dev(rt_dst(rt).dev)) {
+ struct sw_flow_key flow_key;
+ struct vport *dst_vport;
+ struct sk_buff *skb;
+ int err;
+ int flow_key_len;
+ struct sw_flow *flow;
+
+ dst_vport = ovs_internal_dev_get_vport(rt_dst(rt).dev);
+ if (!dst_vport)
+ goto done;
+
+ skb = alloc_skb(cache->len, GFP_ATOMIC);
+ if (!skb)
+ goto done;
+
+ __skb_put(skb, cache->len);
+ memcpy(skb->data, get_cached_header(cache), cache->len);
+
+ err = ovs_flow_extract(skb, dst_vport->port_no, &flow_key,
+ &flow_key_len);
+
+ consume_skb(skb);
+ if (err)
+ goto done;
+
+ flow = ovs_flow_tbl_lookup(rcu_dereference(dst_vport->dp->table),
+ &flow_key, flow_key_len);
+ if (flow) {
+ cache->flow = flow;
+ ovs_flow_hold(flow);
+ }