]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
xsk: add umem fill queue support and mmap
authorMagnus Karlsson <magnus.karlsson@intel.com>
Wed, 2 May 2018 11:01:24 +0000 (13:01 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 3 May 2018 22:55:23 +0000 (15:55 -0700)
Here, we add another setsockopt for registered user memory (umem)
called XDP_UMEM_FILL_QUEUE. Using this socket option, the process can
ask the kernel to allocate a queue (ring buffer) and also mmap it
(XDP_UMEM_PGOFF_FILL_QUEUE) into the process.

The queue is used to explicitly pass ownership of umem frames from the
user process to the kernel. These frames will in a later patch be
filled in with Rx packet data by the kernel.

v2: Fixed potential crash in xsk_mmap.

Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/uapi/linux/if_xdp.h
net/xdp/Makefile
net/xdp/xdp_umem.c
net/xdp/xdp_umem.h
net/xdp/xsk.c
net/xdp/xsk_queue.c [new file with mode: 0644]
net/xdp/xsk_queue.h [new file with mode: 0644]

index 41252135a0fe2be5853292502d93fd1099af863e..975661e1bacab0b8da41833109d682cb20333140 100644 (file)
@@ -23,6 +23,7 @@
 
 /* XDP socket options */
 #define XDP_UMEM_REG                   3
+#define XDP_UMEM_FILL_RING             4
 
 struct xdp_umem_reg {
        __u64 addr; /* Start of packet data area */
@@ -31,4 +32,18 @@ struct xdp_umem_reg {
        __u32 frame_headroom; /* Frame head room */
 };
 
+/* Pgoff for mmaping the rings */
+#define XDP_UMEM_PGOFF_FILL_RING       0x100000000
+
+struct xdp_ring {
+       __u32 producer __attribute__((aligned(64)));
+       __u32 consumer __attribute__((aligned(64)));
+};
+
+/* Used for the fill and completion queues for buffers */
+struct xdp_umem_ring {
+       struct xdp_ring ptrs;
+       __u32 desc[0] __attribute__((aligned(64)));
+};
+
 #endif /* _LINUX_IF_XDP_H */
index a5d736640a0f790d73c1dbe6de19264f24a12aca..074fb2b2d51ca152a46833804be241d3b2b0fedb 100644 (file)
@@ -1,2 +1,2 @@
-obj-$(CONFIG_XDP_SOCKETS) += xsk.o xdp_umem.o
+obj-$(CONFIG_XDP_SOCKETS) += xsk.o xdp_umem.o xsk_queue.o
 
index ec8b3552be448900960f681fdfa390ef42b42a45..e1f627d0cc1cb6ff6705e9d204c84e7391aa6017 100644 (file)
@@ -65,6 +65,11 @@ static void xdp_umem_release(struct xdp_umem *umem)
        struct task_struct *task;
        struct mm_struct *mm;
 
+       if (umem->fq) {
+               xskq_destroy(umem->fq);
+               umem->fq = NULL;
+       }
+
        if (umem->pgs) {
                xdp_umem_unpin_pages(umem);
 
index 4597ae81a221b152c7b132315d60487866dd5097..25634b8a5c6f33148fe6c58e2a407aef78541351 100644 (file)
 #include <linux/if_xdp.h>
 #include <linux/workqueue.h>
 
+#include "xsk_queue.h"
 #include "xdp_umem_props.h"
 
 struct xdp_umem {
+       struct xsk_queue *fq;
        struct page **pgs;
        struct xdp_umem_props props;
        u32 npgs;
index 84e0e867febbac001bdd901510251c281eae4821..da67a3c5c1c90f8898d90bb8b9824e11d873e4f5 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/netdevice.h>
 #include <net/xdp_sock.h>
 
+#include "xsk_queue.h"
 #include "xdp_umem.h"
 
 static struct xdp_sock *xdp_sk(struct sock *sk)
@@ -39,6 +40,21 @@ static struct xdp_sock *xdp_sk(struct sock *sk)
        return (struct xdp_sock *)sk;
 }
 
+static int xsk_init_queue(u32 entries, struct xsk_queue **queue)
+{
+       struct xsk_queue *q;
+
+       if (entries == 0 || *queue || !is_power_of_2(entries))
+               return -EINVAL;
+
+       q = xskq_create(entries);
+       if (!q)
+               return -ENOMEM;
+
+       *queue = q;
+       return 0;
+}
+
 static int xsk_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
@@ -101,6 +117,23 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
                mutex_unlock(&xs->mutex);
                return 0;
        }
+       case XDP_UMEM_FILL_RING:
+       {
+               struct xsk_queue **q;
+               int entries;
+
+               if (!xs->umem)
+                       return -EINVAL;
+
+               if (copy_from_user(&entries, optval, sizeof(entries)))
+                       return -EFAULT;
+
+               mutex_lock(&xs->mutex);
+               q = &xs->umem->fq;
+               err = xsk_init_queue(entries, q);
+               mutex_unlock(&xs->mutex);
+               return err;
+       }
        default:
                break;
        }
@@ -108,6 +141,36 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
        return -ENOPROTOOPT;
 }
 
+static int xsk_mmap(struct file *file, struct socket *sock,
+                   struct vm_area_struct *vma)
+{
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       unsigned long size = vma->vm_end - vma->vm_start;
+       struct xdp_sock *xs = xdp_sk(sock->sk);
+       struct xsk_queue *q = NULL;
+       unsigned long pfn;
+       struct page *qpg;
+
+       if (!xs->umem)
+               return -EINVAL;
+
+       if (offset == XDP_UMEM_PGOFF_FILL_RING)
+               q = xs->umem->fq;
+       else
+               return -EINVAL;
+
+       if (!q)
+               return -EINVAL;
+
+       qpg = virt_to_head_page(q->ring);
+       if (size > (PAGE_SIZE << compound_order(qpg)))
+               return -EINVAL;
+
+       pfn = virt_to_phys(q->ring) >> PAGE_SHIFT;
+       return remap_pfn_range(vma, vma->vm_start, pfn,
+                              size, vma->vm_page_prot);
+}
+
 static struct proto xsk_proto = {
        .name =         "XDP",
        .owner =        THIS_MODULE,
@@ -131,7 +194,7 @@ static const struct proto_ops xsk_proto_ops = {
        .getsockopt =   sock_no_getsockopt,
        .sendmsg =      sock_no_sendmsg,
        .recvmsg =      sock_no_recvmsg,
-       .mmap =         sock_no_mmap,
+       .mmap =         xsk_mmap,
        .sendpage =     sock_no_sendpage,
 };
 
diff --git a/net/xdp/xsk_queue.c b/net/xdp/xsk_queue.c
new file mode 100644 (file)
index 0000000..23da4f2
--- /dev/null
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/* XDP user-space ring structure
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/slab.h>
+
+#include "xsk_queue.h"
+
+static u32 xskq_umem_get_ring_size(struct xsk_queue *q)
+{
+       return sizeof(struct xdp_umem_ring) + q->nentries * sizeof(u32);
+}
+
+struct xsk_queue *xskq_create(u32 nentries)
+{
+       struct xsk_queue *q;
+       gfp_t gfp_flags;
+       size_t size;
+
+       q = kzalloc(sizeof(*q), GFP_KERNEL);
+       if (!q)
+               return NULL;
+
+       q->nentries = nentries;
+       q->ring_mask = nentries - 1;
+
+       gfp_flags = GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN |
+                   __GFP_COMP  | __GFP_NORETRY;
+       size = xskq_umem_get_ring_size(q);
+
+       q->ring = (struct xdp_ring *)__get_free_pages(gfp_flags,
+                                                     get_order(size));
+       if (!q->ring) {
+               kfree(q);
+               return NULL;
+       }
+
+       return q;
+}
+
+void xskq_destroy(struct xsk_queue *q)
+{
+       if (!q)
+               return;
+
+       page_frag_free(q->ring);
+       kfree(q);
+}
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
new file mode 100644 (file)
index 0000000..7eb556b
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * XDP user-space ring structure
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _LINUX_XSK_QUEUE_H
+#define _LINUX_XSK_QUEUE_H
+
+#include <linux/types.h>
+#include <linux/if_xdp.h>
+
+#include "xdp_umem_props.h"
+
+struct xsk_queue {
+       struct xdp_umem_props umem_props;
+       u32 ring_mask;
+       u32 nentries;
+       u32 prod_head;
+       u32 prod_tail;
+       u32 cons_head;
+       u32 cons_tail;
+       struct xdp_ring *ring;
+       u64 invalid_descs;
+};
+
+struct xsk_queue *xskq_create(u32 nentries);
+void xskq_destroy(struct xsk_queue *q);
+
+#endif /* _LINUX_XSK_QUEUE_H */