diff mbox series

[RFC,17/24] xsk: introduce xsk_buff_pool

Message ID 20180131135356.19134-18-bjorn.topel@gmail.com
State RFC, archived
Delegated to: David Miller
Headers show
Series Introducing AF_XDP support | expand

Commit Message

Björn Töpel Jan. 31, 2018, 1:53 p.m. UTC
From: Björn Töpel <bjorn.topel@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.topel@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 mbox series

Patch

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_ */