int flags, int *addr_len);
 int tcp_set_rcvlowat(struct sock *sk, int val);
 void tcp_data_ready(struct sock *sk);
+int tcp_mmap(struct file *file, struct socket *sock,
+            struct vm_area_struct *vma);
 void tcp_parse_options(const struct net *net, const struct sk_buff *skb,
                       struct tcp_options_received *opt_rx,
                       int estab, struct tcp_fastopen_cookie *foc);
 
 }
 EXPORT_SYMBOL(tcp_set_rcvlowat);
 
+/* When user wants to mmap X pages, we first need to perform the mapping
+ * before freeing any skbs in receive queue, otherwise user would be unable
+ * to fallback to standard recvmsg(). This happens if some data in the
+ * requested block is not exactly fitting in a page.
+ *
+ * We only support order-0 pages for the moment.
+ * mmap() on TCP is very strict, there is no point
+ * trying to accommodate with pathological layouts.
+ */
+int tcp_mmap(struct file *file, struct socket *sock,
+            struct vm_area_struct *vma)
+{
+       unsigned long size = vma->vm_end - vma->vm_start;
+       unsigned int nr_pages = size >> PAGE_SHIFT;
+       struct page **pages_array = NULL;
+       u32 seq, len, offset, nr = 0;
+       struct sock *sk = sock->sk;
+       const skb_frag_t *frags;
+       struct tcp_sock *tp;
+       struct sk_buff *skb;
+       int ret;
+
+       if (vma->vm_pgoff || !nr_pages)
+               return -EINVAL;
+
+       if (vma->vm_flags & VM_WRITE)
+               return -EPERM;
+       /* TODO: Maybe the following is not needed if pages are COW */
+       vma->vm_flags &= ~VM_MAYWRITE;
+
+       lock_sock(sk);
+
+       ret = -ENOTCONN;
+       if (sk->sk_state == TCP_LISTEN)
+               goto out;
+
+       sock_rps_record_flow(sk);
+
+       if (tcp_inq(sk) < size) {
+               ret = sock_flag(sk, SOCK_DONE) ? -EIO : -EAGAIN;
+               goto out;
+       }
+       tp = tcp_sk(sk);
+       seq = tp->copied_seq;
+       /* Abort if urgent data is in the area */
+       if (unlikely(tp->urg_data)) {
+               u32 urg_offset = tp->urg_seq - seq;
+
+               ret = -EINVAL;
+               if (urg_offset < size)
+                       goto out;
+       }
+       ret = -ENOMEM;
+       pages_array = kvmalloc_array(nr_pages, sizeof(struct page *),
+                                    GFP_KERNEL);
+       if (!pages_array)
+               goto out;
+       skb = tcp_recv_skb(sk, seq, &offset);
+       ret = -EINVAL;
+skb_start:
+       /* We do not support anything not in page frags */
+       offset -= skb_headlen(skb);
+       if ((int)offset < 0)
+               goto out;
+       if (skb_has_frag_list(skb))
+               goto out;
+       len = skb->data_len - offset;
+       frags = skb_shinfo(skb)->frags;
+       while (offset) {
+               if (frags->size > offset)
+                       goto out;
+               offset -= frags->size;
+               frags++;
+       }
+       while (nr < nr_pages) {
+               if (len) {
+                       if (len < PAGE_SIZE)
+                               goto out;
+                       if (frags->size != PAGE_SIZE || frags->page_offset)
+                               goto out;
+                       pages_array[nr++] = skb_frag_page(frags);
+                       frags++;
+                       len -= PAGE_SIZE;
+                       seq += PAGE_SIZE;
+                       continue;
+               }
+               skb = skb->next;
+               offset = seq - TCP_SKB_CB(skb)->seq;
+               goto skb_start;
+       }
+       /* OK, we have a full set of pages ready to be inserted into vma */
+       for (nr = 0; nr < nr_pages; nr++) {
+               ret = vm_insert_page(vma, vma->vm_start + (nr << PAGE_SHIFT),
+                                    pages_array[nr]);
+               if (ret)
+                       goto out;
+       }
+       /* operation is complete, we can 'consume' all skbs */
+       tp->copied_seq = seq;
+       tcp_rcv_space_adjust(sk);
+
+       /* Clean up data we have read: This will do ACK frames. */
+       tcp_recv_skb(sk, seq, &offset);
+       tcp_cleanup_rbuf(sk, size);
+
+       ret = 0;
+out:
+       release_sock(sk);
+       kvfree(pages_array);
+       return ret;
+}
+EXPORT_SYMBOL(tcp_mmap);
+
 static void tcp_update_recv_tstamps(struct sk_buff *skb,
                                    struct scm_timestamping *tss)
 {