From: Björn Töpel <bjorn.to...@intel.com>

The xsk_buff_pool is a buff_pool implementation, that uses frames
(buffs) provided by userspace instead of the page allocator.

Signed-off-by: Björn Töpel <bjorn.to...@intel.com>
---
 net/xdp/Makefile        |   2 +-
 net/xdp/xsk_buff_pool.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++
 net/xdp/xsk_buff_pool.h |  17 ++++
 3 files changed, 243 insertions(+), 1 deletion(-)
 create mode 100644 net/xdp/xsk_buff_pool.c
 create mode 100644 net/xdp/xsk_buff_pool.h

diff --git a/net/xdp/Makefile b/net/xdp/Makefile
index b9d5d6b8823c..42727a32490c 100644
--- a/net/xdp/Makefile
+++ b/net/xdp/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_XDP_SOCKETS) += xsk.o xsk_ring.o xsk_packet_array.o
+obj-$(CONFIG_XDP_SOCKETS) += xsk.o xsk_ring.o xsk_packet_array.o 
xsk_buff_pool.o
diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
new file mode 100644
index 000000000000..b0760ba2b188
--- /dev/null
+++ b/net/xdp/xsk_buff_pool.c
@@ -0,0 +1,225 @@
+#include "xsk_buff_pool.h"
+
+#include <linux/skbuff.h>
+#include <linux/buff_pool.h>
+
+#include "xsk_packet_array.h" /* XDP_KERNEL_HEADROOM */
+#include "xsk_buff.h"
+#include "xsk_ring.h"
+
+#define BATCH_SIZE 32
+
+static bool xsk_bp_alloc_from_freelist(struct xsk_buff_pool *impl,
+                                      unsigned long *handle)
+{
+       struct xsk_buff *buff;
+
+       if (impl->free_list) {
+               buff = impl->free_list;
+               impl->free_list = buff->next;
+               buff->next = NULL;
+               *handle = (unsigned long)buff;
+
+               return true;
+       }
+
+       return false;
+}
+
+static int xsk_bp_alloc(void *pool, unsigned long *handle)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+       struct xsk_buff *buff;
+       struct xskq_iter it;
+       u32 id;
+
+       if (xsk_bp_alloc_from_freelist(impl, handle))
+               return 0;
+
+       it = xskq_deq_iter(impl->q, BATCH_SIZE);
+
+       while (!xskq_iter_end(&it)) {
+               id = xskq_deq_iter_get_id(impl->q, &it);
+               buff = &impl->bi->buffs[id];
+               buff->next = impl->free_list;
+               impl->free_list = buff;
+               xskq_deq_iter_next(impl->q, &it);
+       }
+
+       xskq_deq_iter_done(impl->q, &it);
+
+       if (xsk_bp_alloc_from_freelist(impl, handle))
+               return 0;
+
+       return -ENOMEM;
+}
+
+static void xsk_bp_free(void *pool, unsigned long handle)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+       struct xsk_buff *buff = (struct xsk_buff *)handle;
+
+       buff->next = impl->free_list;
+       impl->free_list = buff;
+}
+
+static unsigned int xsk_bp_buff_size(void *pool)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+
+       return impl->bi->buff_len - impl->bi->rx_headroom -
+               XDP_KERNEL_HEADROOM;
+}
+
+static unsigned int xsk_bp_total_buff_size(void *pool)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+
+       return impl->bi->buff_len - impl->bi->rx_headroom;
+}
+
+static unsigned int xsk_bp_buff_headroom(void *pool)
+{
+       (void)pool;
+
+       return XSK_KERNEL_HEADROOM;
+}
+
+static unsigned int xsk_bp_buff_truesize(void *pool)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+
+       return impl->bi->buff_len;
+}
+
+static void *xsk_bp_buff_ptr(void *pool, unsigned long handle)
+{
+       struct xsk_buff *buff = (struct xsk_buff *)handle;
+
+       (void)pool;
+       return buff->data + buff->offset;
+}
+
+static int xsk_bp_buff_convert_to_page(void *pool,
+                                      unsigned long handle,
+                                      struct page **pg, unsigned int *pg_off)
+{
+       unsigned int req_len, buff_len, pg_order = 0;
+       void *data;
+
+       buff_len = xsk_bp_total_buff_size(pool);
+       req_len = buff_len + SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       /* XXX too sloppy? clean up... */
+       if (req_len > PAGE_SIZE) {
+               pg_order++;
+               if (req_len > (PAGE_SIZE << pg_order))
+                       return -ENOMEM;
+       }
+
+       *pg = dev_alloc_pages(pg_order);
+       if (unlikely(!*pg))
+               return -ENOMEM;
+
+       data = page_address(*pg);
+       memcpy(data, xsk_bp_buff_ptr(pool, handle),
+              xsk_bp_total_buff_size(pool));
+       *pg_off = 0;
+
+       xsk_bp_free(pool, handle);
+
+       return 0;
+}
+
+static dma_addr_t xsk_bp_buff_dma(void *pool,
+                                 unsigned long handle)
+{
+       struct xsk_buff *buff = (struct xsk_buff *)handle;
+
+       (void)pool;
+       return buff->dma + buff->offset;
+}
+
+static void xsk_bp_buff_dma_sync_cpu(void *pool,
+                                    unsigned long handle,
+                                    unsigned int off,
+                                    unsigned int size)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+       struct xsk_buff *buff = (struct xsk_buff *)handle;
+
+       dma_sync_single_range_for_cpu(impl->bi->dev, buff->dma,
+                                     off, size, impl->bi->dir);
+}
+
+static void xsk_bp_buff_dma_sync_dev(void *pool,
+                                    unsigned long handle,
+                                    unsigned int off,
+                                    unsigned int size)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+       struct xsk_buff *buff = (struct xsk_buff *)handle;
+
+       dma_sync_single_range_for_device(impl->bi->dev, buff->dma,
+                                        off, size, impl->bi->dir);
+}
+
+static void xsk_bp_destroy(void *pool)
+{
+       struct xsk_buff_pool *impl = (struct xsk_buff_pool *)pool;
+       struct xsk_buff *buff = impl->free_list;
+
+       while (buff) {
+               xskq_return_id(impl->q, buff->id);
+               buff = buff->next;
+       }
+
+       kfree(impl);
+}
+
+struct buff_pool *xsk_buff_pool_create(struct xsk_buff_info *buff_info,
+                                      struct xsk_queue *queue)
+{
+       struct buff_pool_ops *pool_ops;
+       struct xsk_buff_pool *impl;
+       struct buff_pool *pool;
+
+       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+       if (!pool)
+               return NULL;
+
+       pool_ops = kzalloc(sizeof(*pool_ops), GFP_KERNEL);
+       if (!pool_ops) {
+               kfree(pool);
+               return NULL;
+       }
+
+       impl = kzalloc(sizeof(*impl), GFP_KERNEL);
+       if (!impl) {
+               kfree(pool_ops);
+               kfree(pool);
+               return NULL;
+       }
+
+       impl->bi = buff_info;
+       impl->q = queue;
+
+       pool_ops->alloc = xsk_bp_alloc;
+       pool_ops->free = xsk_bp_free;
+       pool_ops->buff_size = xsk_bp_buff_size;
+       pool_ops->total_buff_size = xsk_bp_total_buff_size;
+       pool_ops->buff_headroom = xsk_bp_buff_headroom;
+       pool_ops->buff_truesize = xsk_bp_buff_truesize;
+       pool_ops->buff_ptr = xsk_bp_buff_ptr;
+       pool_ops->buff_convert_to_page = xsk_bp_buff_convert_to_page;
+       pool_ops->buff_dma = xsk_bp_buff_dma;
+       pool_ops->buff_dma_sync_cpu = xsk_bp_buff_dma_sync_cpu;
+       pool_ops->buff_dma_sync_dev = xsk_bp_buff_dma_sync_dev;
+       pool_ops->destroy = xsk_bp_destroy;
+
+       pool->pool = impl;
+       pool->ops = pool_ops;
+
+       return pool;
+}
+
diff --git a/net/xdp/xsk_buff_pool.h b/net/xdp/xsk_buff_pool.h
new file mode 100644
index 000000000000..302c3e40cae4
--- /dev/null
+++ b/net/xdp/xsk_buff_pool.h
@@ -0,0 +1,17 @@
+#ifndef XSK_BUFF_POOL_H_
+#define XSK_BUFF_POOL_H_
+
+struct xsk_buff;
+struct xsk_buff_info;
+struct xsk_queue;
+
+struct xsk_buff_pool {
+       struct xsk_buff *free_list;
+       struct xsk_buff_info *bi;
+       struct xsk_queue *q;
+};
+
+struct buff_pool *xsk_buff_pool_create(struct xsk_buff_info *buff_info,
+                                      struct xsk_queue *queue);
+
+#endif /* XSK_BUFF_POOL_H_ */
-- 
2.14.1

Reply via email to