Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[sfrench/cifs-2.6.git] / net / xdp / xsk.c
index 3da0b52f308d4a4f4cd946301987fb246e25b76b..9f13aa3353e31f9692ce41db10a977ac2614d7d8 100644 (file)
@@ -571,6 +571,13 @@ static u32 xsk_get_num_desc(struct sk_buff *skb)
 
 static void xsk_destruct_skb(struct sk_buff *skb)
 {
+       struct xsk_tx_metadata_compl *compl = &skb_shinfo(skb)->xsk_meta;
+
+       if (compl->tx_timestamp) {
+               /* sw completion timestamp, not a real one */
+               *compl->tx_timestamp = ktime_get_tai_fast_ns();
+       }
+
        xsk_cq_submit_locked(xdp_sk(skb->sk), xsk_get_num_desc(skb));
        sock_wfree(skb);
 }
@@ -655,8 +662,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
 static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
                                     struct xdp_desc *desc)
 {
+       struct xsk_tx_metadata *meta = NULL;
        struct net_device *dev = xs->dev;
        struct sk_buff *skb = xs->skb;
+       bool first_frag = false;
        int err;
 
        if (dev->priv_flags & IFF_TX_SKB_NO_LINEAR) {
@@ -687,6 +696,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
                                kfree_skb(skb);
                                goto free_err;
                        }
+
+                       first_frag = true;
                } else {
                        int nr_frags = skb_shinfo(skb)->nr_frags;
                        struct page *page;
@@ -709,12 +720,45 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
 
                        skb_add_rx_frag(skb, nr_frags, page, 0, len, 0);
                }
+
+               if (first_frag && desc->options & XDP_TX_METADATA) {
+                       if (unlikely(xs->pool->tx_metadata_len == 0)) {
+                               err = -EINVAL;
+                               goto free_err;
+                       }
+
+                       meta = buffer - xs->pool->tx_metadata_len;
+                       if (unlikely(!xsk_buff_valid_tx_metadata(meta))) {
+                               err = -EINVAL;
+                               goto free_err;
+                       }
+
+                       if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) {
+                               if (unlikely(meta->request.csum_start +
+                                            meta->request.csum_offset +
+                                            sizeof(__sum16) > len)) {
+                                       err = -EINVAL;
+                                       goto free_err;
+                               }
+
+                               skb->csum_start = hr + meta->request.csum_start;
+                               skb->csum_offset = meta->request.csum_offset;
+                               skb->ip_summed = CHECKSUM_PARTIAL;
+
+                               if (unlikely(xs->pool->tx_sw_csum)) {
+                                       err = skb_checksum_help(skb);
+                                       if (err)
+                                               goto free_err;
+                               }
+                       }
+               }
        }
 
        skb->dev = dev;
        skb->priority = READ_ONCE(xs->sk.sk_priority);
        skb->mark = READ_ONCE(xs->sk.sk_mark);
        skb->destructor = xsk_destruct_skb;
+       xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta);
        xsk_set_destructor_arg(skb);
 
        return skb;
@@ -1282,6 +1326,14 @@ struct xdp_umem_reg_v1 {
        __u32 headroom;
 };
 
+struct xdp_umem_reg_v2 {
+       __u64 addr; /* Start of packet data area */
+       __u64 len; /* Length of packet data area */
+       __u32 chunk_size;
+       __u32 headroom;
+       __u32 flags;
+};
+
 static int xsk_setsockopt(struct socket *sock, int level, int optname,
                          sockptr_t optval, unsigned int optlen)
 {
@@ -1325,8 +1377,10 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
 
                if (optlen < sizeof(struct xdp_umem_reg_v1))
                        return -EINVAL;
-               else if (optlen < sizeof(mr))
+               else if (optlen < sizeof(struct xdp_umem_reg_v2))
                        mr_size = sizeof(struct xdp_umem_reg_v1);
+               else if (optlen < sizeof(mr))
+                       mr_size = sizeof(struct xdp_umem_reg_v2);
 
                if (copy_from_sockptr(&mr, optval, mr_size))
                        return -EFAULT;