*     Functions provided by ip_sockglue.c
  */
 
-extern int     ip_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+extern void    ipv4_pktinfo_prepare(struct sk_buff *skb);
 extern void    ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
 extern int     ip_cmsg_send(struct net *net,
                             struct msghdr *msg, struct ipcm_cookie *ipc);
 
 /*
  *     SOL_IP control messages.
  */
+#define PKTINFO_SKB_CB(__skb) ((struct in_pktinfo *)((__skb)->cb))
 
 static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
 {
-       struct in_pktinfo info;
-       struct rtable *rt = skb_rtable(skb);
+       struct in_pktinfo info = *PKTINFO_SKB_CB(skb);
 
        info.ipi_addr.s_addr = ip_hdr(skb)->daddr;
-       if (rt) {
-               info.ipi_ifindex = rt->rt_iif;
-               info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
-       } else {
-               info.ipi_ifindex = 0;
-               info.ipi_spec_dst.s_addr = 0;
-       }
 
        put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
 }
 }
 
 /**
- * ip_queue_rcv_skb - Queue an skb into sock receive queue
+ * ipv4_pktinfo_prepare - transfert some info from rtable to skb
  * @sk: socket
  * @skb: buffer
  *
- * Queues an skb into socket receive queue. If IP_CMSG_PKTINFO option
- * is not set, we drop skb dst entry now, while dst cache line is hot.
+ * To support IP_CMSG_PKTINFO option, we store rt_iif and rt_spec_dst
+ * in skb->cb[] before dst drop.
+ * This way, receiver doesnt make cache line misses to read rtable.
  */
-int ip_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+void ipv4_pktinfo_prepare(struct sk_buff *skb)
 {
-       if (!(inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO))
-               skb_dst_drop(skb);
-       return sock_queue_rcv_skb(sk, skb);
+       struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb);
+       const struct rtable *rt = skb_rtable(skb);
+
+       if (rt) {
+               pktinfo->ipi_ifindex = rt->rt_iif;
+               pktinfo->ipi_spec_dst.s_addr = rt->rt_spec_dst;
+       } else {
+               pktinfo->ipi_ifindex = 0;
+               pktinfo->ipi_spec_dst.s_addr = 0;
+       }
+       skb_dst_drop(skb);
 }
-EXPORT_SYMBOL(ip_queue_rcv_skb);
 
 int ip_setsockopt(struct sock *sk, int level,
                int optname, char __user *optval, unsigned int optlen)
 
        if (inet_sk(sk)->inet_daddr)
                sock_rps_save_rxhash(sk, skb);
 
-       rc = ip_queue_rcv_skb(sk, skb);
+       rc = sock_queue_rcv_skb(sk, skb);
        if (rc < 0) {
                int is_udplite = IS_UDPLITE(sk);
 
 
        rc = 0;
 
+       ipv4_pktinfo_prepare(skb);
        bh_lock_sock(sk);
        if (!sock_owned_by_user(sk))
                rc = __udp_queue_rcv_skb(sk, skb);