net: add a hardware buffer management helper API
authorGregory CLEMENT <gregory.clement@free-electrons.com>
Mon, 14 Mar 2016 08:39:04 +0000 (09:39 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Mar 2016 16:19:46 +0000 (12:19 -0400)
This basic implementation allows to share code between driver using
hardware buffer management. As the code is hardware agnostic, there is
few helpers, most of the optimization brought by the an HW BM has to be
done at driver level.

Tested-by: Sebastian Careba <nitroshift@yahoo.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/hwbm.h [new file with mode: 0644]
net/Kconfig
net/core/Makefile
net/core/hwbm.c [new file with mode: 0644]

diff --git a/include/net/hwbm.h b/include/net/hwbm.h
new file mode 100644 (file)
index 0000000..47d0866
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _HWBM_H
+#define _HWBM_H
+
+struct hwbm_pool {
+       /* Capacity of the pool */
+       int size;
+       /* Size of the buffers managed */
+       int frag_size;
+       /* Number of buffers currently used by this pool */
+       int buf_num;
+       /* constructor called during alocation */
+       int (*construct)(struct hwbm_pool *bm_pool, void *buf);
+       /* protect acces to the buffer counter*/
+       spinlock_t lock;
+       /* private data */
+       void *priv;
+};
+#ifdef CONFIG_HWBM
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf);
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp);
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp);
+#else
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {}
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; }
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
+{ return 0; }
+#endif /* CONFIG_HWBM */
+#endif /* _HWBM_H */
index 10640d5f8beefd84d41b8d3eff2758c3757e9f6b..e13449870d0684dc3266945158b6ede5c340c146 100644 (file)
@@ -253,6 +253,9 @@ config XPS
        depends on SMP
        default y
 
+config HWBM
+       bool
+
 config SOCK_CGROUP_DATA
        bool
        default n
index 014422e2561fcb19bb1c3fd7bc9cf8989aa1a7dd..d6508c2ddca502800181a4c4fa49c92235edcebc 100644 (file)
@@ -25,4 +25,5 @@ obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
 obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
 obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
 obj-$(CONFIG_DST_CACHE) += dst_cache.o
+obj-$(CONFIG_HWBM) += hwbm.o
 obj-$(CONFIG_NET_DEVLINK) += devlink.o
diff --git a/net/core/hwbm.c b/net/core/hwbm.c
new file mode 100644 (file)
index 0000000..941c284
--- /dev/null
@@ -0,0 +1,87 @@
+/* Support for hardware buffer manager.
+ *
+ * Copyright (C) 2016 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/skbuff.h>
+#include <net/hwbm.h>
+
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
+{
+       if (likely(bm_pool->frag_size <= PAGE_SIZE))
+               skb_free_frag(buf);
+       else
+               kfree(buf);
+}
+EXPORT_SYMBOL_GPL(hwbm_buf_free);
+
+/* Refill processing for HW buffer management */
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
+{
+       int frag_size = bm_pool->frag_size;
+       void *buf;
+
+       if (likely(frag_size <= PAGE_SIZE))
+               buf = netdev_alloc_frag(frag_size);
+       else
+               buf = kmalloc(frag_size, gfp);
+
+       if (!buf)
+               return -ENOMEM;
+
+       if (bm_pool->construct)
+               if (bm_pool->construct(bm_pool, buf)) {
+                       hwbm_buf_free(bm_pool, buf);
+                       return -ENOMEM;
+               }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hwbm_pool_refill);
+
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
+{
+       int err, i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bm_pool->lock, flags);
+       if (bm_pool->buf_num == bm_pool->size) {
+               pr_warn("pool already filled\n");
+               return bm_pool->buf_num;
+       }
+
+       if (buf_num + bm_pool->buf_num > bm_pool->size) {
+               pr_warn("cannot allocate %d buffers for pool\n",
+                       buf_num);
+               return 0;
+       }
+
+       if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
+               pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
+                       buf_num,  bm_pool->buf_num);
+               return 0;
+       }
+
+       for (i = 0; i < buf_num; i++) {
+               err = hwbm_pool_refill(bm_pool, gfp);
+               if (err < 0)
+                       break;
+       }
+
+       /* Update BM driver with number of buffers added to pool */
+       bm_pool->buf_num += i;
+
+       pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
+       spin_unlock_irqrestore(&bm_pool->lock, flags);
+
+       return i;
+}
+EXPORT_SYMBOL_GPL(hwbm_pool_add);